Promise.resolve().then与setImmediate和nextTick的区别

41

NodeJS 0.11以及io.js和Node 0.12分支都内置了原生的 promises。

原生的 promises 具有.then方法,总是在未来的事件循环周期中执行。

迄今为止,自从我从nextTick转换后,我一直在使用setImmediate将事物排队到下一个事件循环迭代中:

setImmediate(deferThisToNextTick); // My NodeJS 0.10 code
process.nextTick(deferThisToNextTick); // My NodeJS 0.8 code

由于我们现在有了一种新的方法来做到这一点:

Promise.resolve().then(deferThisToNextTick); 

我应该使用哪个?另外 - Promise.resolve.then 是否像setImmediate 或者像nextTick 一样在事件循环前或后运行代码?


分享一些研究:在Chromium中,一个Promise会进入一个微任务队列,这里有运行微任务的代码:https://github.com/yoavweiss/Blink/blob/80ec93f1d58a0a0ce99a07ed07c203a106b2a88c/Source/core/dom/Microtask.cpp 当脚本到达或者被检查时,如果没有脚本正在运行,就会调用这个代码:https://github.com/yoavweiss/Blink/blob/5be896b969d644c18facc30833210b07f43ae086/Source/core/html/parser/HTMLScriptRunner.cpp - Benjamin Gruenbaum
Promise resolution https://github.com/joyent/node/blob/857975d5e7e0d7bf38577db0478d9e5ede79922e/deps/v8/src/promise.js#L154 - 调用 https://github.com/joyent/node/blob/857975d5e7e0d7bf38577db0478d9e5ede79922e/deps/v8/src/execution.cc#L321 - 到达 https://github.com/joyent/node/blob/857975d5e7e0d7bf38577db0478d9e5ede79922e/deps/v8/src/execution.cc#L310 - Benjamin Gruenbaum
2
非常相关:https://github.com/joyent/node/issues/7714 - Benjamin Gruenbaum
更多内容请查阅: https://github.com/joyent/node/blob/v0.12/src/node.js#L305 - Benjamin Gruenbaum
已验证的微任务在nextTick队列上运行:https://github.com/joyent/node/blob/v0.12/src/node.js#L326-L329,我将等待几个小时,看看是否有v8或node团队成员愿意澄清,如果没有,我会写出我的答案。 - Benjamin Gruenbaum
你可以在那个 Github 讨论中分享这个问题,这样他们就可以来这里回答了。 - thefourtheye
3个回答

36

使用Promise.resolve().thennextTick相比没有任何优势。它们运行在同一个队列上,但具有稍高的优先级,即promise处理程序可以阻止下一次tick回调从运行,反之则不行。这种行为是实现细节,不应该依赖它。

Promise.resolve().then显然较慢(我认为要慢很多),因为它创建了两个将被丢弃的promise。

您可以在此处找到广泛的实现信息:https://github.com/joyent/node/pull/8325

最重要的部分:Promise.resolve().then类似于nextTick,而不是setImmediate。在使用它代替setImmediate时,可能会大大改变代码行为。


1
点赞。我很想看看最后一段的例子。 - thefourtheye
谢谢,这听起来正确,也是我在代码中观察到的。如果您能详细说明何时使用其中之一以及为什么会这样工作,那就太好了。@thefourtheye 有没有任何例子可以说明 nextTick 不适用而 setImmediate 适用 - 也不会是 Promise.resolve().then - Benjamin Gruenbaum
1
它运行在同一个队列上,但具有稍微更高的优先级...实际上并不是这样。每个回调都有自己处理队列的机制。 - Trevor Norris
好的,微任务队列在nextTick队列中被清空,这就是我的意思。我的意图是尽可能使其行为像一个队列。 - vkurchatkin
@vkurchatkin 为什么会创建两个 Promise?"Promise.resolve().then 显然更慢(我认为是很多),因为它创建了两个将被丢弃的 Promise"? - human
显示剩余2条评论

10

我不会回答加粗的技术问题,只回答问题:

我应该使用哪一个?

我认为除非你对异步执行函数的结果感兴趣,否则没有理由使用Promise.resolve().then()。当然,如果你感兴趣,那么这比处理回调地狱或从setTimeoutnextTick创建新的Promise要好得多。

还有第二个技术上的区别,比时间更重要:Promise会吞掉异常。这可能不是你想要的。所以,就像@vkurchatkin提到的那样,不要创建只用来抛出的Promise。这不仅因为它速度较慢,而且会使你的代码变得难以阅读,你的应用程序也会变得更容易出错。


-5

Promise.resolve 会立即(同步地)被解决,而 setImmediate 则是在当前事件执行后显式地立即执行。


2
尝试在您的浏览器控制台中运行以下代码,以查看您的错误:Promise.resolve([]).then(function(){ console.log('1'); }); console.log('2'); - Martin Konecny
我认为你正在将其与新的Promise((res,rej)=>res())混淆。上面的例子new Promise((res) => { console.log(1); res(); }); console.log(2);。在这种情况下,1会在2之前被打印出来。 - Trenskow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接