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

标签 javascript node.js event-loop

我正在研究 Node.js 中 libuv 提供的事件循环。我遇到了following blog by 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);

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

这可能是什么解释?
谢谢。

最佳答案

如果您查看 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);
  }
}

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

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

上面的函数是从 main part 调用的。使用此代码的事件循环:
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

}

请注意对 uv_update_time(loop) 的调用在调用 uv__run_timers() 之前.这设置了 uv__run_timers() 的计时器引用。这里是 the code对于 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;
}

关于javascript - nodejs(libuv)事件循环是否在一个阶段(队列)中执行所有回调,然后再移动到下一个阶段或以循环方式运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60538323/

相关文章:

javascript - 如何使用流在 node.js 中提取 .tar.bz2?

asynchronous - Dart 中事件循环何时启动以及事件队列如何工作

javascript - 如何确保加载自定义字体

javascript - 如何在 Cypress 中测试下拉列表(选择)框中的所有选项?

javascript - 使用 Youtube API key 进行搜索时如何解决错误 403()?

node.js - 将 Nuxt JS SSR 应用部署到 Google Cloud App Engine Standard

node.js - Nodejs Restful 认证

python - ZeroMQ 轮询器 vs Tornados EventLoop

c++ - 在每个应用程序的事件循环迭代中执行槽

javascript - 如何在 IE Windows Mobile 5 中向 <SELECT> 添加选项