JavaScript的事件迴圈與巨集微任務
本文記錄了作者在研究JS的事件迴圈(event loop)
和巨集任務和微任務
的過程中如何抽絲剝繭,理清原理的。
閱讀本文大概需要二十分鐘。
一. 單執行緒與多執行緒
JavaScript的設計就是為了處理瀏覽器網頁的互動,如果有多個執行緒同時操作DOM,那網頁的渲染需要涉及執行緒安全的問題,不好控制。
JavaScript是單執行緒的,那麼處理任務是一件接著一件處理,從上往下順序執行:
console.log('script start')
console.log('do something...')
console.log('script end')
// script start
// do something...
// script end
那如果一個任務耗時很久的話,如:網路請求、定時器、io處理等,後面的任務也會阻塞。
那麼,JavaScript是如何處理的呢?
console.log('script start')
console.log('do something...')
setTimeout(() => {
console.log('timer over')
}, 1000)
// 點選頁面
console.log('click page')
console.log('script end')
// script start
// do something...
// click page
// script end
// timer over
由上面的例子,可以看出,顯然JS程式碼不是從上往下順序執行的。time over
在time end
後面執行。
其實,
JavaScript單執行緒指的是執行環境中負責解釋和執行JavaScript程式碼的只有一個執行緒,即為JS引擎執行緒。
但是執行環境程式中是有多個執行緒的。
以瀏覽器為例,其一個程式中包括如下執行緒:
- JS引擎執行緒
- 事件觸發執行緒
- 定時器觸發執行緒
- 非同步http請求執行緒
- GUI渲染執行緒
當遇到非同步任務時,JS引擎會把它交給執行環境去處理,而JS引擎執行緒繼續解釋執行後面的任務,這樣就實現了非同步非阻塞。
非同步任務執行完成後,會把它的回撥加到訊息佇列中,JS引擎在合適的時機從訊息佇列中取出訊息並執行。
二. 同步和非同步
同步程式碼如下:
console.log('hello 0')
console.log('hello 1')
console.log('hello 2')
// hello 0
// hello 1
// hello 2
它們會依次執行,執行完了就返回結果。
而非同步程式碼呢?
setTimeout(() => {
console.log('hello 0')
}, 1000)
console.log('hello 1')
// hello 1
// hello 0
上面的setTimeout會發起一個非同步任務,不會阻塞程式碼。
三. 事件迴圈與訊息佇列
前面說到JS執行環境是多執行緒的,有一個執行緒(事件觸發執行緒)專門負責事件迴圈機制和訊息佇列維護。
JS引擎在遇到非同步任務時,會交給相應的執行緒單獨去維護非同步任務,等待某個時機(定時結束、網路請求完成…),然後由事件觸發執行緒將非同步任務的回撥加入到訊息佇列中,訊息佇列中的訊息等待被執行。
同時,JS引擎會維護一個執行棧,同步程式碼會依次加入到執行棧,結束會退出執行棧。
如果執行棧裡的任務執行完成(執行棧為空),事件觸發執行緒才會從訊息佇列中取出任務,放到執行棧去執行。事件觸發執行緒會迴圈從訊息佇列中取任務,然後放到執行棧去執行,這種機制叫做事件迴圈機制。
四. 巨集任務和微任務
以上機制在ES5的情況下已經夠用了,但ES6會有一些問題。
console.log('script start')
setTimeout(function() {
console.log('timer over')
}, 0)
Promise.resolve().then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('script end')
// script start
// script end
// promise1
// promise2
// timer over
這裡promise1
和promise2
在time over
之前列印了??
這裡涉及一個知識點:巨集任務
和微任務
。
所有非同步任務分為巨集任務和微任務:
- 巨集任務:setTimeout、setInterval等
- 微任務:Promise、process.nextTick等
五. 參考
https://juejin.im/post/59e21e8551882578db27c364
https://segmentfault.com/a/1190000015559210
https://juejin.im/post/5be5a0b96fb9a049d518febc
https://juejin.im/post/5a6547d0f265da3e283a1df7#heading-6
https://www.jianshu.com/p/e073808c26e4
https://segmentfault.com/a/1190000016022069
https://segmentfault.com/a/1190000011198232
http://web.jobbole.com/84351/
https://segmentfault.com/a/1190000014242281
https://segmentfault.com/a/1190000005173218
https://github.com/jawil/Node.js/issues/2
https://juejin.im/post/5be5a0b96fb9a049d518febc
相關文章
- 總結:JavaScript非同步、事件迴圈與訊息佇列、微任務與巨集任務JavaScript非同步事件佇列
- JS事件迴圈機制(event loop)之巨集任務/微任務JS事件OOP
- JavaScript的巨集任務與微任務JavaScript
- javascript事件環微任務和巨集任務佇列原理JavaScript事件佇列
- JavaScript巨集任務和微任務JavaScript
- 事件迴圈與任務佇列事件佇列
- 任務佇列,巨集任務與微任務佇列
- 微任務、巨集任務與Event-LoopOOP
- 巨集任務和微任務
- async與await以及巨集微任務AI
- 【譯】理解Javascript函式執行—呼叫棧、事件迴圈、任務等JavaScript函式事件
- 聊聊Javascript的事件迴圈JavaScript事件
- JS中EventLoop、巨集任務與微任務的個人理解JSOOP
- 關於非同步方法中的巨集任務與微任務非同步
- js中的巨集任務和微任務JS
- Event Loop、 巨集任務和微任務OOP
- 瞭解js執行機制——微任務與巨集任務JS
- macrotask 巨集任務 + microtask 微任務區別Mac
- JavaScript事件迴圈機制JavaScript事件
- JavaScript事件迴圈(Event Loop)JavaScript事件OOP
- Javascript 事件迴圈event loopJavaScript事件OOP
- JavaScript 事件迴圈機制JavaScript事件
- JavaScript-事件迴圈-eventLoopJavaScript事件OOP
- 我理解的javascript事件迴圈(一)JavaScript事件
- 從面試題看 JS 事件迴圈與 macro micro 任務佇列面試題JS事件Mac佇列
- [譯]深入理解JavaScript函式執行—呼叫棧,事件迴圈和任務等JavaScript函式事件
- node基礎面試事件環?微任務、巨集任務?一篇帶你飛面試事件
- javascript事件迴圈機制EventLoopJavaScript事件OOP
- javascript之事件迴圈機制JavaScript事件
- js事件迴圈與macroµ任務佇列-前端面試進階JS事件Mac佇列前端面試
- JavaScript的事件迴圈機制淺析JavaScript事件
- 淺析JavaScript的事件迴圈機制JavaScript事件
- JavaScript的事件迴圈(Event loop)(附圖)JavaScript事件OOP
- 詳談javascript和node的事件迴圈JavaScript事件
- 微任務和巨集任務哪個先執行
- 學習JavaScript非同步、事件迴圈JavaScript非同步事件
- JavaScript 事件迴圈詳解(翻譯)JavaScript事件
- javascript事件迴圈(瀏覽器/node)JavaScript事件瀏覽器