JavaScript 中的工作佇列與Promise

,發表於2017-09-24

在 JS 中,“事件迴圈(Event Loop)”的概念相信大家都不陌生。在 ES6 中,在事件迴圈佇列之上引入了一層新概念,稱為“工作佇列(Job queue)”,這個概念在 Promise 的非同步行為中有用到。恰好在網上看到一篇文章,通過一小段程式對這兩個概念進行了展示,對我很有幫助,分享給大家。注意以下並非對原文的逐字翻譯。

預備知識:

  1. 事件迴圈(Event Loop)(參見《你不知道的 JavaScript (中卷) 》第一章)
  2. 工作佇列(Job Queue)(參見《你不知道的 JavaScript (中卷) 》第一章)
  3. 在 Promise 上呼叫 then(..) 的時候,提供給 then(..) 的回撥函式總是被非同步呼叫(排程到工作佇列中),即使 Promise 已經被解析了。
  4. setTimeout(..) 中的回撥函式也總是被非同步呼叫,不過是安排在了事件迴圈的下一個 tick 中。

示例

const p = new Promise(
    // this is called the "executor"
    (resolve, reject) => {
        console.log(1);
        resolve(2);
        console.log(3);result
    }
);

console.log(4);

p.then(
    // this is called the success handler
    result => console.log(result)
);

setTimeout(() => console.log(5), 0);

console.log(6);

上面程式碼的輸出結果是 1 3 4 6 2 5

解釋

第一步:Executor

Executor (見程式碼註釋)總是被立即執行。當新建立 Promise 的時候,傳給構造器的函式總是在當前 tick 中立馬執行。所以 1 被列印出來, resolve 函式被呼叫,Promise 被解析,之後沒有觸發新的事件。然後 3 被列印。當前 tick 繼續往下走,跳出 Promise 的建構函式,列印出 4

第二步: .then

接下來執行 .then(..)。Promise p 已經被解析,處理成功狀態的回撥函式會被排程到工作佇列中執行。

第三步: 工作佇列

事件迴圈中的每一個 tick 都會擁有自己的工作佇列(Job Queue),工作佇列中的資訊會在當前 tick 的末尾,下一個 tick 之前被處理,所以此時 result(=2) 不會被列印。

第四步:事件迴圈佇列

setTimeout(..) 和事件監聽中的回撥函式會被排程到事件迴圈佇列中,即便是延遲時間為 0

第五步:接下來

.thensetTimeout(..) 語句分別被排程到工作佇列和事件迴圈佇列中,二者都會非同步執行。當前 tick 繼續前進,列印出 6

然而當前 tick 並沒有結束,因為工作佇列中依然有事件需要處理。接下來程式會執行被排程到工作佇列中處理成功狀態的回撥函式,輸出 2。此時佇列中已經沒有其他事件,當前 tick 結束。

接下來程式進入下一個 tick ,5 被輸出,整個程式結束。


走完這個例子,相信大家對事件迴圈及工作佇列的概念和 Promise 的運作機制,有了更加深入的理解。 (完)

參考資料:

  1. 《你不知道的 JavaScript (中卷) 》第1章和第3章

  2. http://www.reactjunkie.com/promises-promises/

相關文章