Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/398.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/39.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 为什么nodejs事件循环在第一次运行后是不确定的?_Javascript_Node.js_Event Loop - Fatal编程技术网

Javascript 为什么nodejs事件循环在第一次运行后是不确定的?

Javascript 为什么nodejs事件循环在第一次运行后是不确定的?,javascript,node.js,event-loop,Javascript,Node.js,Event Loop,Node.js说: 计时器的执行顺序将根据调用它们的上下文而有所不同。如果两者都是从主模块中调用的,则计时将受到进程性能的限制(这可能会受到机器上运行的其他应用程序的影响) 例如,如果我们运行不在I/O周期内的以下脚本(即主模块),则两个计时器的执行顺序是不确定的,因为它受进程性能的约束: // timeout_vs_immediate.js setTimeout(function timeout () { console.log('timeout'); },0); setImmediate

Node.js说:

计时器的执行顺序将根据调用它们的上下文而有所不同。如果两者都是从主模块中调用的,则计时将受到进程性能的限制(这可能会受到机器上运行的其他应用程序的影响)

例如,如果我们运行不在I/O周期内的以下脚本(即主模块),则两个计时器的执行顺序是不确定的,因为它受进程性能的约束:

// timeout_vs_immediate.js
setTimeout(function timeout () {
 console.log('timeout');
},0);

setImmediate(function immediate () {
  console.log('immediate');
});

$ node timeout_vs_immediate.js
timeout
immediate

$ node timeout_vs_immediate.js
immediate
timeout
在同一页中,我想我理解了事件循环是如何工作的,但是在这次主运行之后,为什么偶数循环不能正确地完成它的工作呢?与I/O周期相比,有什么不同呢?

正如Node.js doc所说:

计时器的执行顺序将根据调用它们的上下文而有所不同。如果两者都是从主模块中调用的,则计时将受到进程性能的限制(这可能会受到机器上运行的其他应用程序的影响)。 例如,如果我们运行不在I/O周期内的以下脚本(即主模块),则两个计时器的执行顺序是不确定的,因为它受进程性能的约束:

// timeout_vs_immediate.js
setTimeout(function timeout () {
 console.log('timeout');
},0);

setImmediate(function immediate () {
  console.log('immediate');
});

$ node timeout_vs_immediate.js
timeout
immediate

$ node timeout_vs_immediate.js
immediate
timeout
为什么

node.js中的每个事件都由libuv的
uv\u run()
函数驱动。部分代码

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;

  r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);

  while (r != 0 && loop->stop_flag == 0) {
    uv__update_time(loop);
    uv__run_timers(loop);
    ran_pending = uv__run_pending(loop);
    uv__run_idle(loop);
    uv__run_prepare(loop);

    ......

    uv__io_poll(loop, timeout);
    uv__run_check(loop);
    uv__run_closing_handles(loop);
    ............
正如node.js文档所解释的,我们可以匹配代码中事件循环的每个阶段

定时器相位
uv运行定时器(循环)

I/O回调
运行挂起=uv运行挂起(循环)

空闲/准备
uv\u运行\u空闲(循环);uv\u运行\u准备(循环)

轮询
uv\uuu io\u轮询(循环,超时)

检查
紫外线运行检查(循环)

关闭回调
uv\u运行\u关闭\u句柄(循环)

多相 但如果我们在循环进入计时器阶段之前仔细查看代码,它会调用
uv\u更新时间(循环)以初始化循环时间

void uv_update_time(uv_loop_t* loop) {
   uv__update_time(loop);
}
所发生的是
uv\uu更新\u时间(循环)
调用函数
uv\uu时间

UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
  /* Use a fast time source if available.  We only need millisecond precision.
   */
  loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
}
对uv\uuu hr时间的调用取决于平台,并且是cpu耗时的工作,因为它使系统 打电话给我。它受到计算机上运行的其他应用程序的影响

#define NANOSEC ((uint64_t) 1e9)
uint64_t uv__hrtime(uv_clocktype_t type) {
  struct timespec ts;
  clock_gettime(CLOCK_MONOTONIC, &ts);
  return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
一旦返回,将在事件循环中调用计时器阶段

void uv__run_timers(uv_loop_t* loop) {
  ...
  for (;;) {
    ....
    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)
      break;
      ....
    uv_timer_again(handle);
    handle->timer_cb(handle);
  }
}
如果循环的当前时间大于超时,则运行计时器阶段的回调。 另一个需要注意的重要事项是
setTimeout
当设置为
0
时,会在内部转换为
1
。 同样由于
hr\u time
以纳秒为单位的返回时间,这种行为如
timeout\u vs\u immediate.js
所示,现在变得更加容易解释

如果第一个循环之前的准备工作花费了超过
1ms
的时间,则
计时器
阶段调用与其相关联的回调。如果小于
1ms
事件循环将继续到下一阶段,并在循环的检查阶段运行
setImmediate
回调,在 循环的下一个滴答声

希望这能澄清
setTimeout
setImmediate
在主模块内调用时的非确定性行为