nodejs (libuv) 事件循环是否在一个阶段(队列)中执行所有回调,然后再进入下一阶段或以循环方式运行?

use*_*980 4 javascript event-loop node.js

我正在研究 Node.js 中 libuv 提供的事件循环。我看到了Deepal Jayasekara 的以下博客,还在 YouTube 上看到了 Bert Belder 和 Daniel Khan 的解释。

有一点我不清楚 - 根据我的理解,事件循环在进入另一阶段之前处理一个阶段的所有项目。因此,如果是这种情况,如果 setTimeout 阶段不断添加回调,我应该能够阻止事件循环。

然而,当我尝试复制这一点时,它并没有发生。以下是代码:

var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.write('Hello World!');
  console.log("Response sent");
  res.end();
}).listen(8081);


setInterval(() => {
  console.log("Entering for loop");
// Long running loop that allows more callbacks to get added to the setTimeout phase before this callback's processing completes
 for (let i = 0; i < 7777777777; i++); 
 console.log("Exiting for loop");
}, 0);
Run Code Online (Sandbox Code Playgroud)

事件循环似乎以循环方式运行。它首先执行在我向服务器发送请求之前添加的回调,然后处理请求,然后继续回调。感觉就像一个队列正在运行。据我所知,没有一个队列,所有过期的计时器回调都应该在进入下一阶段之前首先执行。因此,上面的代码片段应该无法返回 Hello World 响应。

对此可能的解释是什么?谢谢。

jfr*_*d00 5

如果您查看 libuv 本身,您会发现事件循环中运行计时器的操作部分是函数uv_run_timers()

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min(timer_heap(loop));
    if (heap_node == NULL)
      break;

    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)
      break;

    uv_timer_stop(handle);
    uv_timer_again(handle);
    handle->timer_cb(handle);
  }
}
Run Code Online (Sandbox Code Playgroud)

它的工作方式是事件循环在当前时间设置一个时间标记,然后依次处理该时间到期的所有计时器,而不更新循环时间。因此,这将触发所有已经超过其时间的计时器,但不会触发在处理已经到期的计时器时到期的任何新计时器。

这会导致更公平的调度,因为它运行所有到期的计时器,然后运行事件循环中其余类型的事件,然后返回以再次执行任何到期的计时器。这不会处理任何在此事件循环周期开始时未到期但在处理其他计时器时到期的计时器。因此,您会看到您所询问的行为。

使用以下代码从事件循环的主要部分调用上述函数:

int uv_run(uv_loop_t *loop, uv_run_mode mode) {
  DWORD 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);                    <==  establish loop time
    uv__run_timers(loop);                    <==  process only timers due by that loop time

    ran_pending = uv_process_reqs(loop);
    uv_idle_invoke(loop);
    uv_prepare_invoke(loop);

 .... more code here

}
Run Code Online (Sandbox Code Playgroud)

uv_update_time(loop)请注意在调用 之前先调用right uv__run_timers()。这设置了引用的计时器uv__run_timers()。这是代码uv_update_time()

void uv_update_time(uv_loop_t* loop) {
  uint64_t new_time = uv__hrtime(1000);
  assert(new_time >= loop->time);
  loop->time = new_time;
}
Run Code Online (Sandbox Code Playgroud)