node Promise如何介入`nextTick`和`setImmediate`之间的?

4

我在我的应用程序中遇到了一个奇怪的时间错误,这是从Bluebird切换到原生Promise导致的。我已经修复了它,但留下了这种古怪的现象:原生Promise似乎在nextTicksetImmediate之间挤进去了 - 这是怎么回事?这是否应该发生?承诺应该与这些有关吗?

~function(){
  setTimeout            (console.log.bind(console, 'timeout A'));
  process.nextTick      (console.log.bind(console, 'nextTick A'));
  setImmediate          (console.log.bind(console, 'setImmediate A'));
  Promise.resolve().then(console.log.bind(console, 'promise'));
  process.nextTick      (console.log.bind(console, 'nextTick B'));
  setImmediate          (console.log.bind(console, 'setImmediate B'));
  setTimeout            (console.log.bind(console, 'timeout B'));
}();

本机产量:

nextTick A
nextTick B
promise undefined
setImmediate A
setImmediate B
timeout A
timeout B

蓝鸟产生了如下结果:
nextTick A
nextTick B
setImmediate A
promise undefined
setImmediate B
timeout A
timeout B

我认为没有明确的答案。显然,它们的定时是通过本地代码接近事件循环完成的(而Bluebird使用setImmediate)。我认为没有任何规范告诉我们应该发生什么,实现(node V8)可以自由决定其认为合理的事情。 - Bergi
@Bergi - 考虑到 nextTicksetImmediate 是 Node 的构造,而不是 V8 的一部分,我认为 Node 应该以可靠的方式定义它。 - user578895
实际上,setImmediate 在浏览器中也是可用的,并且有一个草案将其纳入 HTML5 规范。但是,是 Node 需要并且确实定义了它,因此它应该像现在这样工作。关于此事,Node 可能有一些文档,您是在寻找这些吗? - Bergi
@Bergi 嗯,DOM规范_规定_承诺使用微任务语义运行。 Node必须对此做出选择,并选择微任务语义。顺便说一下,setImmediate在节点和浏览器中有所不同。实际上没有文档,只有大约10-20人知道并关心它:) 如果您认为值得记录拉取请求将很好。 - Benjamin Gruenbaum
@BenjaminGruenbaum - 在 Github 上的一次对话似乎暗示 Node 没有做出任何选择,而是使用微任务的 V8。这方面有没有文档资料? - user578895
@BenjaminGruenbaum:啊,我已经很久没有看DOM规范了,关于微任务和Promise任务的引用对我来说是新的。你手头有链接吗? - Bergi
1个回答

3
原生的Promise似乎在nextTick和setImmediate之间挤了进去——怎么会这样?这是否是预期行为?关于这个问题,Promise应该放在哪里?
是的,Promise在微任务nextTick队列之后运行,在任何任务(如setImmediate)执行之前。
这是它们的预期行为,也是我们在NodeJS中期望它们做的事情。这是这里决定的,你可以在这里阅读相关内容。
Bluebird的不同行为
Bluebird的行为早于原生的Promise,Bluebird 3.0使用nextTick和微任务语义进行调度。使用Promise.setSchedulernextTick作为调度程序(而不是setImmediate),您可以手动覆盖此行为。
您可以在这里查看代码
GlobalSetImmediate.call(global, fn)

请注意,您的代码无论如何都不应依赖这些行为。

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