我正在研究 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/