在节点文档here中,可以说一起调用setTimeout和setImmidiate的输出是不确定的。
我明白那个,
但是下面是使用IO包装器回调的示例
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
这使得订单始终为:
sesetImmidiate
setTimeout
有以下说明:
与setTimeout()相比,使用setImmediate()的主要优点是,如果在I / O周期内安排了任何计时器,则setImmediate()将始终在任何计时器之前执行,而与存在多少计时器无关。
为什么恰好“如果在I / O周期内安排了setImmediate(),它将始终在任何计时器之前执行”?
最佳答案
这是由于libuv
设计的原因,在this文章中,您可以找到有关其工作原理的完整说明,以下是摘要:
Libuv订单执行:
while (r != 0 && loop->stop_flag == 0) {
// first timers
uv__update_time(loop);
uv__run_timers(loop);
ran_pending = uv__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) {
timeout = uv_backend_timeout(loop);
}
uv__io_poll(loop, timeout);
uv__run_check(loop); // check handlers - "setImmediate"
uv__run_closing_handles(loop);
if (mode == UV_RUN_ONCE) {
// second timers
uv__update_time(loop);
uv__run_timers(loop);
}
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
uv__loop_alive-检查是否有任何要引用的处理程序
调用,或任何活动操作挂起
uv__update_time —此
将发送系统调用以获取当前时间并更新循环
时间(用于标识过期的计时器)。
uv__run_timers —运行
所有过期的计时器
uv__run_pending-运行所有已完成/错误的I / O
回调
uv__io_poll — I / O的轮询
uv__run_check —运行所有检查
处理程序(setImmediate回调将在此处运行)
uv__run_closing_handles —运行所有关闭处理程序
setTimeout
和setImmediate
都是宏任务,这就是为什么要按此顺序执行的原因,有关此here的讨论很好:如果脚本已由setImmediate()安排,则“轮询”阶段将
将超时设置为零,这意味着在队列已
筋疲力尽,“轮询”阶段将不会等待将回调添加到
排队,但继续进行检查阶段。
如果脚本已经
由setTimeout()安排,“轮询”将设置一个超时,即
计时器最快阈值减去当前时间的结果。
超时,循环继续进行,最后回绕到计时器
相。
关于javascript - 为什么setTimeout和setiImidiate仅在IO周期中是确定性的,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56153710/