搜尋
首頁web前端js教程JS與Node.js中的事件循環詳解

JS與Node.js中的事件循環詳解

Dec 13, 2017 am 09:30 AM
javascriptnode.js詳解

js中的event loop,引出了chrome與node中運行具有setTimeoutPromise的程式時候執行結果不一樣的問題,從而引出了Nodejs的event loop機制,這篇文章透過實例給大家詳細分析了JS與Node.js中的事件的原理以及用法,希望能幫助大家。

console.log(1)
setTimeout(function() {
 new Promise(function(resolve, reject) {
 console.log(2)
 resolve()
 })
 .then(() => {
 console.log(3)
 })
}, 0)
setTimeout(function() {
 console.log(4)
}, 0)
// chrome中运行:1 2 3 4
// Node中运行: 1 2 4 3

chrome和Node執行的結果不一樣,這就很有趣了。

1. JS 中的任務佇列

#JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那麼,為什麼JavaScript不能有多個執行緒呢?這樣能提高效率啊。

JavaScript的單線程,與它的用途有關。作為瀏覽器腳本語言,JavaScript的主要用途是與使用者互動,以及操作DOM。這決定了它只能是單線程,否則會帶來複雜的同步問題。例如,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上加入內容,另一個線程就刪除了這個節點,這時瀏覽器應該以哪個執行緒為準?

所以,為了避免複雜性,從一誕生,JavaScript就是單線程,這已經變成了這門語言的核心特徵,將來也不會改變。

為了利用多核心CPU的運算能力,HTML5提出Web Worker標準,允許JavaScript腳本建立多個線程,但是子執行緒完全受主執行緒控制,且不得操作DOM。所以,這個新標準並沒有改變JavaScript單執行緒的本質。

2. 任務佇列event loop

#單一執行緒就意味著,所有任務都需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就得一直等著。

於是,所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)。同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;非同步任務指的是,不進入主執行緒、而進入"任務佇列"(task queue)的任務,只有"任務隊列"通知主線程,某個非同步任務可以執行了,該任務才會進入主執行緒執行。

具體來說,非同步執行的運作機制如下。 (同步執行也是如此,因為它可以被視為沒有非同步任務的非同步執行。)

所有同步任務都在主執行緒上執行,形成一個執行堆疊(execution context stack)。主執行緒之外,還存在一個"任務隊列"(task queue)。只要非同步任務有了運行結果,就在"任務隊列"之中放置一個事件。一旦"執行堆疊"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。主執行緒不斷重複上面的第三步。

只要主執行緒空了,就會去讀取"任務佇列",這就是JavaScript的運作機制。這個過程會不斷重複。

3. 定時器 setTimeoutsetInterval

##定時器功能主要由

setTimeout()setInterval()這兩個函數來完成,它們的內部運作機製完全一樣,差別在於前者指定的程式碼是一次執行,後者則為重複執行。

setTimeout(fn,0)的意思是,指定某個任務在主執行緒最早可得的空閒時間執行,也就是說,盡可能早得執行。它在"任務佇列"的尾部新增一個事件,因此要等到同步任務和"任務佇列"現有的事件都處理完,才會執行。

HTML5標準規定了setTimeout()的第二個參數的最小值(最短間隔),不得低於4毫秒,如果低於這個值,就會自動增加。在此之前,舊版的瀏覽器都會將最短間隔設為10毫秒。另外,對於那些DOM的變動(尤其是涉及頁面重新渲染的部分),通常不會立即執行,而是每16毫秒執行一次。這時使用requestAnimationFrame()的效果比setTimeout()##。

要注意的是,setTimeout()只是將事件插入了"任務佇列",必須等到目前程式碼(執行堆疊)執行完,主執行緒才會去執行它指定的回呼函數。要是當前程式碼耗時很長,有可能要等很久,所以沒有辦法保證,回呼函數一定會在setTimeout()指定的時間執行。

4. Node.js的Event Loop#

事件輪詢主要是針對事件隊列進行輪詢,事件生產者將事件排隊放入隊列中,隊列另外一端有一個線程稱為事件消費者會不斷查詢隊列中是否有事件,如果有事件,就立即會執行,為了防止執行過程中有堵塞操作影響目前執行緒讀取佇列,事件消費者執行緒會委託一個執行緒池專門執行這些堵塞操作。

Javascript前端和Node.js的機制類似這個事件輪詢模型,有的人認為Node.js是單線程,也就是事件消費者是單線程不斷輪詢,如果有堵塞操作怎麼辦,不是堵塞了當前單線程的執行嗎?

其實Node.js底層也有一個線程池,線程池專門用來執行各種堵塞操作,這樣就不會影響單線程這個主線程進行隊列中事件輪詢和一些任務執行,執行緒池操作完以後,又會作為事件生產者將操作結果放入同一個佇列中。

總之,一個事件輪詢Event Loop需要三個元件:

事件佇列Event Queue,屬於FIFO模型,一端推入事件數據,另外一端拉出事件數據,兩端只透過這個隊列通訊,屬於一種非同步的鬆散耦合。隊列的讀取輪詢線程,事件的消費者,Event Loop的主角。單獨執行緒池​​Thread Pool,專門用來執行長任務,重任務,乾繁重體力活的。

Node.js也是單執行緒的Event Loop,但是它的運作機制不同於瀏覽器環境。

根據上圖,Node.js的運作機制如下。

V8引擎解析JavaScript腳本。解析後的程式碼,呼叫Node API。 libuv函式庫負責Node API的執行。它將不同的任務分配給不同的線程,形成一個Event Loop(事件循環),以非同步的方式將任務的執行結果傳回V8引擎。 V8引擎再將結果回傳給使用者。

我們可以看到node.js的核心其實是libuv這個函式庫。這個函式庫是c寫的,它可以使用多執行緒技術,而我們的Javascript應用是單執行緒的。

Nodejs 的非同步任務執行流程:

使用者寫的程式碼是單執行緒的,但nodejs內部並不是單一執行緒!

事件機制:

Node.js不是用多個執行緒為每個請求執行工作的,相反而是它把所有工作加入到一個事件佇列中,然後有一個單獨線程,來循環提取隊列中的事件。事件循環執行緒抓取事件佇列中最上面的條目,執行它,然後抓取下一個條目。當執行長期運行或有阻塞I/O的程式碼時

在Node.js中,因為只有一個單執行緒不斷地輪詢佇列中是否有事件,對於資料庫檔案系統等I/O操作,包括HTTP請求等等這些容易堵塞等待的操作,如果也是在這個單執行緒中實現,肯定會堵塞影響其他工作任務的執行,Javascript/Node.js會委託給底層的執行緒池執行,並會告訴執行緒池一個回調函數,這樣單線程繼續執行其他事情,當這些堵塞操作完成後,其結果與提供的回調函數一起再放入隊列中,當單線程從隊列中不斷讀取事件,讀取到這些堵塞的操作結果後,會將這些操作結果作為回呼函數的輸入參數,然後啟動運行回呼函數。

請注意,Node.js的這個單線程不只是負責讀取隊列事件,還會執行運行回調函數,這是它區別於多線程模式的一個主要特點,多線程模式下,單執行緒只負責讀取佇列事件,不再做其他事情,會委託其他執行緒做其他事情,特別是多核心的情況下,一個CPU核負責讀取佇列事件,一個CPU核負責執行啟動的任務,這種方式最適合很耗費CPU運算的任務。反過來,Node..js的執行啟動任務也就是回呼函數中的任務還是在負責輪詢的單執行緒中執行,這就注定了它不能執行CPU繁重的任務,例如JSON轉換為其他資料格式等等,這些任務會影響事件輪詢的效率。

5. Nodejs特性

#NodeJS的顯著特性:非同步機制、事件驅動。

事件輪詢的整個過程沒有阻塞新使用者的連接,也不需要維護連接。基於這樣的機制,理論上陸續有使用者請求連接,NodeJS都可以回應,因此NodeJS能支援比Java、php程式更高的並發量。

雖然維護事件佇列也需要成本,再由於NodeJS是單線程,事件佇列越長,得到回應的時間就越長,並發量上去還是會力不從心。

RESTful API是NodeJS最理想的应用场景,可以处理数万条连接,本身没有太多的逻辑,只需要请求API,组织数据进行返回即可。

6. 实例

看一个具体实例:

console.log('1')
setTimeout(function() {
 console.log('2')
 new Promise(function(resolve) {
 console.log('4')
 resolve()
 }).then(function() {
 console.log('5')
 })
 setTimeout(() => {
 console.log('haha')
 })
 new Promise(function(resolve) {
 console.log('6')
 resolve()
 }).then(function() {
 console.log('66')
 })
})
setTimeout(function() {
 console.log('hehe')
}, 0)
new Promise(function(resolve) {
 console.log('7')
 resolve()
}).then(function() {
 console.log('8')
})
setTimeout(function() {
 console.log('9')
 new Promise(function(resolve) {
 console.log('11')
 resolve()
 }).then(function() {
 console.log('12')
 })
})
new Promise(function(resolve) {
 console.log('13')
 resolve()
}).then(function() {
 console.log('14')
})
// node1 : 1,7,13,8,14,2,4,6,hehe,9,11,5,66,12,haha // 结果不稳定
// node2 : 1,7,13,8,14,2,4,6,hehe,5,66,9,11,12,haha // 结果不稳定
// node3 : 1,7,13,8,14,2,4,6,5,66,hehe,9,11,12,haha // 结果不稳定
// chrome : 1,7,13,8,14,2,4,6,5,66,hehe,9,11,12,haha


chrome的运行比较稳定,而node环境下运行不稳定,可能会出现两种情况。

chrome运行的结果的原因是Promiseprocess.nextTick()的微任务Event Queue运行的权限比普通宏任务Event Queue权限高,如果取事件队列中的事件的时候有微任务,就先执行微任务队列里的任务,除非该任务在下一轮的Event Loop中,微任务队列清空了之后再执行宏任务队列里的任务。

相关推荐:

Node.js事件循环教程

javascript事件循环之强制梳理

深入理解Node.js 事件循环和回调函数

以上是JS與Node.js中的事件循環詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs. JavaScript:性能和效率注意事項Python vs. JavaScript:性能和效率注意事項Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

JavaScript的起源:探索其實施語言JavaScript的起源:探索其實施語言Apr 29, 2025 am 12:51 AM

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

幕後:什麼語言能力JavaScript?幕後:什麼語言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來:趨勢和預測Python和JavaScript的未來:趨勢和預測Apr 27, 2025 am 12:21 AM

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。