如果使用 CommonJS 模块,使用 setImmediate 调用 setTimeout 会出现不可预测的行为。但如果切换到ESM(package.json中的“type”:“module”),它总是会在setTimeout之前执行setImmediate。
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
Run Code Online (Sandbox Code Playgroud)
为什么会出现这种情况?这与 ESM 模块异步加载有关吗?
推迟执行函数(例如在自定义事件处理中)是JavaScript中的常见模式(例如,请参见此处).曾经是使用setTimeout(myFunc,0)是唯一的方法,但是有了承诺,现在有另一种选择:Promise.resolve().then(myFunc).
我曾经假设这些几乎可以做同样的事情,但是在处理包含自定义事件的库时,我想我会发现是否存在差异,所以我将以下块放入节点:
var logfn=function(v){return function(){console.log(v)}};
setTimeout(logfn(1),0);
Promise.resolve().then(logfn(2));
logfn(3)();
Run Code Online (Sandbox Code Playgroud)
我期待在控制台上看到3,1,2但是我看到了3,2,1.所以换句话说,Promise并不等同于使用setTimeout并且首先从块中出来.至少在Node中.
我在Chrome和Firefox中重复测试结果相同,但是在Edge中它出现了3,1,2.我还希望非原生的promise库在引擎盖下使用setTimeout,这样就会像Edge一样.
是什么决定了这些来电被解决的顺序?这些不同的环境使用什么模型来确定执行顺序?上述任何一种是否代表标准或非标准行为?
PS我坚决不建议依赖任何这种保持一致,我只是好奇.
在下面给出的答案指出了正确的方向,并且如下面的评论中简要提到的那样,我在Jake Archibald的一篇优秀文章中找到了完整的答案(示例几乎与我上面的代码完全相同),尽管我是加在这里而不是将其隐藏在评论中.
我对 Node.js 文档的以下段落感到困惑。
setImmediate()与setTimeout()...定时器的执行顺序将根据调用它们的上下文而变化。如果两者都是从主模块内部调用的,那么计时将受到进程性能的约束(这可能会受到计算机上运行的其他应用程序的影响)。
例如,如果我们运行以下不在 I/O 周期(即主模块)内的脚本,则两个计时器的执行顺序是不确定的,因为它受到进程性能的约束:
接下来显示以下示例
// timeout_vs_immediate.js
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
Run Code Online (Sandbox Code Playgroud)
$ node timeout_vs_immediate.js
timeout
immediate
$ node timeout_vs_immediate.js
immediate
timeout
Run Code Online (Sandbox Code Playgroud)
我不明白是什么导致结果不确定。由于该timers阶段发生在该check阶段之前,因此安排的回调不应该setTimeout总是在安排的回调之前执行吗setImmediate?我认为事件循环中的阶段顺序不会因为上下文切换或其他原因而改变。
该文件还指出
但是,如果在一个 I/O 周期内移动这两个调用,则始终先执行立即回调:
好的,但是所谓的“I/O 周期”与主模块有何不同?
我知道有很多相关的问题,但所有答案都只是通过引用文档来陈述这一事实,而没有解释非确定性在哪里发挥作用,所以我认为这不是重复的。
javascript ×3
node.js ×3
event-loop ×2
asynchronous ×1
callback ×1
commonjs ×1
es6-modules ×1
es6-promise ×1
events ×1
settimeout ×1