以下全部作废。
一个大神写了清晰易懂的文章:
英文原文:https://blog.insiderattack.ne...
翻译:https://zhuanlan.zhihu.com/p/...
官方文档
具体请看文档,此处做一两处重点摘抄。
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
process.nextTick()
process.nextTick()
从技术上讲不是事件循环的一部分。在每个事件阶段执行完毕,要开始下个事件阶段前,都会执行清空 nextTickQueue
。这里可以将其定义为:是底层 C/C++ 处理的一个过渡,在此期间处理一些需要执行的 JS 。
当使用不当时,将会造成一些问题。
递归地调用 process.nextTick()
将会导致事件循环无法继续,即 "starve" your I/O by making recursive process.nextTick() calls
。
这也说明了, process.nextTick()
中的递归调用将会马上排队,在此次 nextTick 执行期间执行,而不是等执行完下一个事件阶段后的 nextTick 期间执行。
深入阅读
问题 1
官方文档在介绍 setImmediate() vs setTimeout()
时说到
如果运行以下不在 I/O 周期(即主模块)内的脚本,则执行两个计时器的顺序是非确定性的,因为它受进程性能的约束
if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process
此处说到“受进程性能约束”具体是指什么?
详见:https://github.com/nodejs/hel...
摘录:
在 Node 中事件由 uv_run()
驱动,部分源码如下:
int uv_run(uv_loop_t* loop, uv_run_mode mode) {
int 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__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
......
uv__io_poll(loop, timeout);
uv__run_check(loop);
uv__run_closing_handles(loop);
............
对照官方文档可知,while
循环中的处理对应事件循环各个阶段。但在开始 uv__run_timers(loop)
定时器阶段处理之前,调用了这样一段代码 uv__update_time(loop)
。
而这个函数定义如下:
void uv_update_time(uv_loop_t* loop) {
uv__update_time(loop);
}
而 uv__update_time
定义又如下,它调用了一个函数 uv__hrtime
。
UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
/* Use a fast time source if available. We only need millisecond precision.
*/
loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
}
对 uv__hrtime
的调用取决于系统,这是一个 CPU 耗时的工作,需要系统再去调用 clock_gettime
。所以这个函数调用耗时将会受到此时机器上运行的其他程序的影响。
当代码执行到定时器阶段,如果此时已有定时器到了阈值,将会被取出来执行,因此 setImmediate
setTimeout
的回调执行先后取决于当时进程性能。
另:setTimeout
即使设置等待时间为 0 ,其最小等待时间也会被转换成 1ms 。
问题 2
const timer = () => {
setTimeout(timer, 1)
setImmediate(() => console.log('immediate fired')) // this will never be logged
const date = new Date()
while (new Date() - date < 2) {}
}
timer()
老版本 Node 中嵌套调用 setImmediate
会导致事件循环一直被卡在 check
阶段,发生上述 process.nextTick()
中提到的 "starve" 问题。
将 Node 版本切换到 6.9.1
发现确实存在问题,但是后来切换到较新版本例如 8.9.4
已经没有这个问题了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。