事件循环和Promise之间的关系
我很好奇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,因为它更注重事件循环和承诺之间的关系】
每个事件循环都有一个microtask队列和一个macrotask队列。
微任务是最初在微任务队列中排队而不是任务队列的任务。请参阅https://www.w3.org/TR/html51/webappapis.html#microtask-queue。
有两种microtasks的:
- 孤回调microtasks,如
Promise
, - 和化合物microtasks,如
Object.observe
,MutationObserver
和process.nextTick
在Node.js的
而宏任务队列主要包含setTimeout
,setInterval
,setImmediate
,,I/O
。
在事件循环,这两个任务队列将分两步执行:
- 首先,检查是否有宏任务(称之为X)在老宏任务队列中;
- 如果X存在且正在运行,请等待下一步完成;否则,立即进入下一步;
- 其次,运行microtask队列的所有microtask;
- 当运行微任务时,我们仍然可以在队列中添加更多的微操作,这些任务也将运行。
在您的例子:
- 首先,你的承诺初始化
new Promise
和resolve
是同步的; - 然后将一个
setTimeout
宏任务同步添加到macrotask队列; - 然后将microtask
promise.then(function(){})
同步添加到microtask队列中,这个任务会立即运行,因为Promise的初始化和解析是同步的,这个任务在任何macrotask之前运行;所以,console.log中的p1 fulfilled
; - 然后将第二个macrotask
setTimeout
添加到macrotask队列; - 此事件循环结束后,运行两个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
- 首先添加三个宏任务
setTimeout
到宏任务队列,以及一个microtaskpromise.then()
到microtask队列; - 运行macrotask;
- 如果条件true运行所有microtasks,但它是false,所以请转到下一步;
- 运行第二个macrotask;
- 检查承诺是否解决,条件是否成立,然后运行所有的microtasks;
- 继续运行其他macrotasks;
你说'X'会先检查,但是,在步骤2中的'setTimeout'宏任务'X'?为什么不先执行它? – PageYe
因为你的承诺初始化和解析是同步的,所以当在第一步中检查旧的macrotask队列中是否存在一个macrotask(称为X)时,它将返回false。只有在同步代码之后,才会调用'setTimeout'宏任务。 – JiangangXiong
由于w3.org对macrotask队列没有任何说明,macrotask队列和任务队列之间有什么区别?从概念上讲,微任务队列是一种任务队列,还是完全不同? – PageYe
承诺将不会被调用,除非堆栈明确的应用程序代码按照博士Axel Rauschmayer here。
...... Promises/A +规范要求 后者的执行方式总是被使用。它通过以下 requirement(2.2.4)为当时()方法指出这样:
onFulfilled或onRejected不能调用,直到执行 上下文堆栈仅包含平台的代码。
必须注意的是非常重要的:
这意味着,你的代码可以依靠运行至完成语义(如 在第1部分解释)和链接承诺不会饿死其他 任务的处理时间。
我不明白这是如何回答这个问题。在OP的例子中,'then'子句**被**调用,而有未决的'setTimeout'任务,假设这些任务被认为是“应用程序代码”或“平台代码”,我不知道定义 - 他们的意思是什么? – 2017-09-23 05:00:14
你可以想像,在当前[蜱]的端部(https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#处理的第二任务队列process-nexttick)即在正常任务队列中的任何事物之前。 – SpiderPig
'setTimeout'也有一个最小延迟。 – zzzzBov
_“我预计'p1 fulfilled'出现在中间。”_你为什么期待这样的结果? – guest271314