天天看點

eventloop雜談

JavaScript 語言特點

  • 單線程,而這個線程中擁有唯一的一個事件循環。(web worker 這裡不參與讨論)
  • 代碼的執行過程中,除了依靠函數調用棧來搞定函數的執行順序外,還依靠任務隊列(task queue)來搞定另外一些代碼的執行。
  • 一個線程中,事件循環是唯一的,但是任務隊列可以擁有多個。
  • 任務隊列又分為 macro-task(宏任務)與 micro-task(微任務),在最新标準中,它們被分别稱為 task 與 jobs 。
  • setTimeout/Promise 等我們稱之為任務源。而進入任務隊列的是他們指定的具體執行任務。
// setTimeout 中的回調函數才是進入任務隊列的任務
setTimeout(function() {
  console.log('xxxx');
})
           
  • 來自不同任務源的任務會進入到不同的任務隊列。其中 setTimeout 與 setInterval 是同源的。
  • 事件循環的順序,決定了 JavaScript 代碼的執行順序。它從 script (整體代碼)開始第一次循環。之後全局上下文進入函數調用棧。直到調用棧清空(隻剩全局),然後執行所有的 micro-task 。當所有可執行的 micro-task 執行完畢之後。循環再次從 macro-task 開始,找到其中一個任務隊列執行完畢,然後再執行所有的 micro-task,這樣一直循環下去。
  • 其中每一個任務的執行,無論是 macro-task 還是 micro-task,都是借助函數調用棧來完成。

macrotask 與 microtask 類别具體分類

// macrotasks
script(整體代碼), setImmediate, setTimeout, setInterval, I/O, UI rendering

// microtasks
process.nextTick, Promises, Object.observe, MutationObserver
           

DEMO

setImmediate(function () {
    console.log();
}, );
setTimeout(function () {
    console.log();
}, );
new Promise(function (resolve) {
    console.log();
    resolve();
    console.log();
}).then(function () {
    console.log();
});
console.log();
process.nextTick(function () {
    console.log();
});
console.log();
// 3 4 6 8 7 5 1 2
           

執行過程如下:

  1. JavaScript引擎首先會從macrotask queue中取出第一個任務,即 script (整段代碼)
  2. 執行完畢後,将microtask queue中的所有任務取出,按順序全部執;
  3. 然後再從macrotask queue中取下一個,

    執行完畢後,

  4. 再次将microtask queue中的全部取出;

    循環往複,直到兩個queue中的任務都取完。

解釋:

  1. 代碼開始執行時,所有這些代碼在

    macrotask queue

    中,取出來執行之。
  2. 後面遇到了

    setTimeout

    ,又加入到

    macrotask queue

    中,
  3. 然後,遇到了

    promise.then

    ,放入到了另一個隊列

    microtask queue

  4. 整個

    execution context stack

    執行完後,
  5. microtask queue

    中的任務了。

是以

promise.then

的回調比

setTimeout

先執行。

參考

Event loops

question

Q:

process.nextTick

也會放入

microtask quque

,為什麼優先級比

promise.then

高呢?

A:process.nextTick 永遠大于 promise.then

在 Node 中,_tickCallback 在每一次執行完 TaskQueue 中的一個任務後被調用,而這個 _tickCallback 中實質上幹了兩件事:

1. nextTickQueue中所有任務執行掉(長度最大1e4,Node版本v6.9.1)

2. 第一步執行完後執行

_runMicrotasks

函數,執行

microtask

中的部分(

promise.then

注冊的回調)是以很明顯

process.nextTick > promise.then

繼續閱讀