2017-09-23 47 views
0

我很好奇Event Loop和Promise之間的關係。
該演示揭示了這個問題。我期望p1 fulfilled出現在中間, ,因爲他們排列任務到相同的任務隊列,並逐一執行。 事件循環和Promise之間的關係

var p1 = new Promise(function(resolve, reject){ 
    resolve(1) 
}) 
setTimeout(function(){ 
    console.log("will be executed at the top of the next Event Loop") 
},0) 
p1.then(function(value){ 
    console.log("p1 fulfilled") 
}) 
setTimeout(function(){ 
    console.log("will be executed at the bottom of the next Event Loop") 
},0) 

控制檯的結果是:

p1 fulfilled 
will be executed at the top of the next Event Loop 
will be executed at the bottom of the next Event Loop 

The visualized effect顯示promise.then的回調沒有去到事件循環的任務隊列。這是正確的?

【注:現在的問題是不一樣的Promise vs setTimeout,因爲它更注重事件循環和承諾之間的關係】

+0

你可以想像,在當前[蜱]的端部(https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#處理的第二任務隊列process-nexttick)即在正常任務隊列中的任何事物之前。 – SpiderPig

+1

'setTimeout'也有一個最小延遲。 – zzzzBov

+0

_「我預計'p1 fulfilled'出現在中間。」_你爲什麼期待這樣的結果? – guest271314

回答

2

每個事件循環都有一個microtask隊列和一個macrotask隊列。

微任務是最初在微任務隊列中排隊而不是任務隊列的任務。請參閱https://www.w3.org/TR/html51/webappapis.html#microtask-queue

有兩種microtasks的:

  • 孤回調microtasks,如Promise
  • 和化合物microtasks,如Object.observeMutationObserverprocess.nextTick在Node.js的
中的NodeJS

而宏任務隊列主要包含setTimeoutsetIntervalsetImmediate,​​,I/O

在事件循環,這兩個任務隊列將分兩步執行:

  1. 首先,檢查是否有宏任務(稱之爲X)在老宏任務隊列中;
  2. 如果X存在且正在運行,請等待下一步完成;否則,立即進入下一步;
  3. 其次,運行microtask隊列的所有microtask;
  4. 當運行微任務時,我們仍然可以在隊列中添加更多的微操作,這些任務也將運行。

在您的例子:

  1. 首先,你的承諾初始化new Promiseresolve是同步的;
  2. 然後將一個setTimeout宏任務同步添加到macrotask隊列;
  3. 然後將microtask promise.then(function(){})同步添加到microtask隊列中,這個任務會立即運行,因爲Promise的初始化和解析是同步的,這個任務在任何macrotask之前運行;所以,console.log中的p1 fulfilled;
  4. 然後將第二個macrotask setTimeout添加到macrotask隊列;
  5. 此事件循環結束後,運行兩個macrotasks;

此代碼:

setTimeout(function(){ 
    console.log("will be executed at the top of the next Event Loop") 
},0) 
var p1 = new Promise(function(resolve, reject){ 
    setTimeout(function(){resolve(1)},0) 
}); 
setTimeout(function(){ 
    console.log("will be executed at the bottom of the next Event Loop") 
},0) 
for (var i = 0; i < 100; i++) { 
    (function(j){ 
     p1.then(function(value){ 
      console.log("promise then - " + j) 
     }); 
    })(i) 
} 

輸出順序:

will be executed at the top of the next Event Loop 
promise then - 0 
promise then - 1 
promise then - 2 
... 
promise then - 99 
will be executed at the bottom of the next Event Loop 
  1. 首先添加三個宏任務setTimeout到宏任務隊列,以及一個microtask promise.then()到microtask隊列;
  2. 運行macrotask;
  3. 如果條件true運行所有microtasks,但它是false,所以請轉到下一步;
  4. 運行第二個macrotask;
  5. 檢查承諾是否解決,條件是否成立,然後運行所有的microtasks;
  6. 繼續運行其他macrotasks;
+0

你說'X'會先檢查,但是,在步驟2中的'setTimeout'宏任務'X'?爲什麼不先執行它? – PageYe

+0

因爲你的承諾初始化和解析是同步的,所以當在第一步中檢查舊的macrotask隊列中是否存在一個macrotask(稱爲X)時,它將返回false。只有在同步代碼之後,纔會調用'setTimeout'宏任務。 – JiangangXiong

+0

由於w3.org對macrotask隊列沒有任何說明,macrotask隊列和任務隊列之間有什麼區別?從概念上講,微任務隊列是一種任務隊列,還是完全不同? – PageYe

0

承諾將不會被調用,除非堆棧明確的應用程序代碼按照博士Axel Rauschmayer here

...... Promises/A +規範要求 後者的執行方式總是被使用。它通過以下 requirement(2.2.4)爲當時()方法指出這樣:

onFulfilled或onRejected不能調用,直到執行 上下文堆棧僅包含平臺的代碼。

必須注意的是非常重要的:

這意味着,你的代碼可以依靠運行至完成語義(如 在第1部分解釋)和鏈接承諾不會餓死其他 任務的處理時間。

+0

我不明白這是如何回答這個問題。在OP的例子中,'then'子句**被**調用,而有未決的'setTimeout'任務,假設這些任務被認爲是「應用程序代碼」或「平臺代碼」,我不知道定義 - 他們的意思是什麼? – 2017-09-23 05:00:14

相關問題