我正在研究libuv在Node中提供的事件循环。我遇到了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()
。
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);
uv__run_timers(loop);
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()