在文档here的节点中,据说一起调用setTimeout和setImmidiate的输出是不确定的。
我知道,
但是下面是使用IO包装回调的示例
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
这使得订单始终为:
sesetImmidiate
setTimeout
具有以下说明: 与setTimeout()相比,使用setImmediate()的主要优点是,如果在I / O周期内安排了setImmediate(),它将始终在任何计时器之前执行,而与存在的计时器数量无关。
为什么精确地将' setImmediate()如果总是在I / O周期内计划在所有计时器之前执行'?
答案 0 :(得分:1)
这是由于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()安排,“轮询”将设置一个超时时间,即 计时器最快阈值减去当前时间的结果。 超时,循环继续进行,最后回绕到计时器 阶段。