- A+
所属分类:Web前端
timers阶段是Nodejs事件循环中的一个阶段,这一阶段主要是检查是否有到期的定时器,如果有则执行其回调。
相关源码位置:
timers
阶段的代码比较少,这里直接贴出来,你也可以点进去上面的源码看自己感兴趣的部分:
void uv__run_timers(uv_loop_t* loop) { struct heap_node* heap_node; uv_timer_t* handle; struct uv__queue* queue_node; struct uv__queue ready_queue; // 初始化一个空的 ready_queue 队列,用于存放已经到期的定时器 uv__queue_init(&ready_queue); for (;;) { // 获取堆中最小(即最早到期)的定时器节点 heap_node = heap_min(timer_heap(loop)); if (heap_node == NULL) break; // 如果堆是空的(没有定时器),则跳出循环 // 将堆节点转换为 uv_timer_t 类型的定时器句柄 handle handle = container_of(heap_node, uv_timer_t, node.heap); if (handle->timeout > loop->time) break; // 如果当前定时器的超时时间大于当前的循环时间,则跳出循环 // 停止到期的定时器,并将其插入到 ready_queue 队列的尾部 uv_timer_stop(handle); uv__queue_insert_tail(&ready_queue, &handle->node.queue); } // 处理 ready_queue 中的所有定时器 while (!uv__queue_empty(&ready_queue)) { queue_node = uv__queue_head(&ready_queue); // 取出队列头部的节点 uv__queue_remove(queue_node); // 移除该节点 uv__queue_init(queue_node); // 重新初始化节点 handle = container_of(queue_node, uv_timer_t, node.queue); // 将节点转换为定时器句柄 handle // 重新启动定时器,如果是重复定时器,则根据设定的间隔重新计算超时时间 uv_timer_again(handle); // 调用定时器的回调函数,执行定时器到期后的操作 handle->timer_cb(handle); } }
- 重新启动定时器
uv_timer_again
的源码位置:node/deps/uv/src/timer.c at main · nodejs/node (github.com)
内容比较少,这里也直接贴出来:
int uv_timer_again(uv_timer_t* handle) { if (handle->timer_cb == NULL) return UV_EINVAL; if (handle->repeat) { uv_timer_stop(handle); uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat); } return 0; }
可以看到会检查handle->repeat
,这里其实就是setTimeout
和setInterval
的区别了,setInterval
到这里会因为handle->repeat
为true
而重新开启新的一轮计时,而setTimeout
则是直接跳过、结束了。
这里的
handle->repeat
是uint64_t
类型,其实就是timeout
。
这里顺便贴一下uv_timer_start
的代码,感兴趣可以看看。
源码位置:node/deps/uv/src/timer.c at main · nodejs/node (github.com)
int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat) { uint64_t clamped_timeout; // 如果定时器正在关闭或回调函数为 NULL,则返回错误代码 UV_EINVAL if (uv__is_closing(handle) || cb == NULL) return UV_EINVAL; // 停止定时器,以确保定时器在重新启动前没有在运行 uv_timer_stop(handle); // 计算定时器的超时时间 clamped_timeout // 它是当前事件循环时间 handle->loop->time 加上 timeout clamped_timeout = handle->loop->time + timeout; // 如果发生整数溢出,导致 clamped_timeout 小于 timeout, // 则将 clamped_timeout 设置为最大值 UINT64_MAX if (clamped_timeout < timeout) clamped_timeout = (uint64_t) -1; // 设置定时器的回调函数、超时时间和重复间隔 handle->timer_cb = cb; handle->timeout = clamped_timeout; handle->repeat = repeat; // start_id 用于在定时器比较函数 timer_less_than 中作为第二索引进行比较 // 并递增事件循环的计时器计数器 handle->start_id = handle->loop->timer_counter++; // 将定时器插入到事件循环的定时器堆中,并启动定时器句柄 heap_insert(timer_heap(handle->loop), (struct heap_node*) &handle->node.heap, timer_less_than); uv__handle_start(handle); return 0; }
-
clamped_timeout
和timeout
之间的关系:-
timeout
是传递给uv_timer_start
函数的参数,表示定时器的初始延迟时间,以毫秒为单位; -
clamped_timeout
是计算后的绝对时间,表示定时器实际超时的绝对时间点(以事件循环的时间handle->loop->time
为基准)。
-
-
uv_timer_start
的timeoute
参数 和 JS中setTimeout
的delay
参数是等价的。