为什么setTimeout和setiImidiate仅在IO周期中是确定性的

时间:2019-05-15 16:19:18

标签: javascript node.js v8

在文档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周期内计划在所有计时器之前执行'?

1 个答案:

答案 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;
  }
  
      
  1. uv__loop_alive-检查是否有任何引用的处理程序   调用或任何活动中的待处理
  2.   
  3. uv__update_time —此   将发送系统调用以获取当前时间并更新循环   时间(用于标识过期的计时器)。
  4.   
  5. uv__run_timers —运行   所有过期的计时器
  6.   
  7. uv__run_pending-运行所有已完成/错误的I / O   回调
  8.   
  9. uv__io_poll -用于I / O的投票
  10.   
  11. uv__run_check —运行所有检查   处理程序(setImmediate回调将在此处运行)
  12.   
  13. uv__run_closing_handles-运行所有关闭处理程序
  14.   

setTimeoutsetImmediate都是宏任务,这就是为什么要按此顺序执行的原因,对此here的讨论很好:

  

如果脚本是通过 setImmediate()安排的,则“轮询”阶段将   将超时设置为零,这意味着在队列已   筋疲力尽,“轮询”阶段将不会等待将回调添加到   排队,但继续进行检查阶段。

     

如果脚本已经   由 setTimeout()安排,“轮询”将设置一个超时时间,即   计时器最快阈值减去当前时间的结果。   超时,循环继续进行,最后回绕到计时器   阶段。