setTimeout
注册的回调会在事件循环的 timers
、poll
和 closing callbacks
阶段执行。需要注意的是,计时器默认定义的 TIMEOUT_MAX
的取值范围是 [1, 2 ^ 31 - 1],不足 1 或者超过上限都会初始化为 1,也就是说你调用 setTimeout(fn, 0)
和 setTimeout(fn, 1)
的效果是一样的。
process.nextTick
注册的回调会在事件循环的当前阶段结束前执行,而不是只有 poll
、check
阶段才会执行。process
是内核模块,运行时是全局上下文,所以 microtask
只有一个,无论你是在哪个阶段、哪个闭包内用 nextTick
注册的回调都会被 push
到nextTickQueue
,并在事件循环当前阶段结束前执行。
setImmediate
注册的回调会在 check
阶段、check
阶段、check
阶段执行。因为它需要由 check watcher
来执行,check watcher
只在 check
阶段处于 active
状态。与 process.nextTick
不同,setImmediate
因运行时的上下文不同而产生不同的 ImmediateList
,所以 macrotask
可以有多个。setImmediate
会在异常的时候执行 process.nextTick(processImmediate)
,会在当前阶段结束前重新执行一次这个异常任务(即 setImmediate
阶段)。
具体的执行过程参考 node/deps/uv/src/unix/core.c
:
// 332
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);
timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
timeout = uv_backend_timeout(loop);
uv__io_poll(loop, timeout);
uv__run_check(loop);
uv__run_closing_handles(loop);
if (mode == UV_RUN_ONCE) {
/* UV_RUN_ONCE implies forward progress: at least one callback must have
* been invoked when it returns. uv__io_poll() can return without doing
* I/O (meaning: no callbacks) when its timeout expires - which means we
* have pending timers that satisfy the forward progress constraint.
*
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
* the check.
*/
uv__update_time(loop);
uv__run_timers(loop);
}
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
/* The if statement lets gcc compile it to a conditional store. Avoids
* dirtying a cache line.
*/
if (loop->stop_flag != 0)
loop->stop_flag = 0;
return r;
}
具体执行情况参见下面这个例子:
/**
* 执行栈中注册 setTimeout 计时器
*/
setTimeout(function () {
// 4. timers 阶段。timer watcher 遍历计时器 Map 的 key,
// 如果有 key <= timeout,执行该 key 对应的 value(计时器任务);
// 否则等到 poll 阶段再检查一次
console.log('setTimeout');
setTimeout(function () {
// 11. 注册 setTimeout 计时器。UV_RUN_ONCE 模式下,
// 会在循环结束之前再执行时间下限到达的计时器任务,取决于进程性能
// 1 <= TIMEOUT_MAX <= 2 ^ 31 - 1
console.log('setTimeout in setTimeout');
}, 0);
setImmediate(function () {
// 9. 注册 setImmediate 计时器。在当前循环的 check 阶段执行。
// (注:这是新的 ImmediateList,当前循环内有 3 个 ImmediateList 了)
console.log('setImmediate in setTimeout');
});
process.nextTick(function () {
// 6. 为 nextTickQueue 添加任务,timers 阶段结束前唤醒 idle watcher
// idle watcher 检查 nextTickQueue,执行任务
console.log('nextTick in setTimeout');
});
}, 0);
/**
* 执行栈中注册 setImmediate 计时器
*/
setImmediate(function () {
// 7. poll 阶段没有可执行任务,阶段结束前唤醒 idle watcher,idle watcher 继续睡;
// 接着唤醒 check watcher,检测到 ImmediateList 不为空,进入 check 阶段。
// check watcher 执行第一个任务
console.log('setImmediate');
setTimeout(function () {
// 13. 注册 setTimeout 计时器
// 由于机器性能,在循环结束前才执行
console.log('setTimeout in setImmediate');
}, 0);
setImmediate(function () {
// 12. 为当前 ImmediateList 添加任务
// 由于机器性能优越,前面 nextTickQueue 为空了,直接进入 check 阶段
console.log('setImmediate in setImmediate');
});
process.nextTick(function () {
// 10. 为 nextTickQueue 添加任务,当所有 ImmediateList 的队首任务都执行完毕时,
// 唤醒 idle watcher,检查 nextTickQueue,执行队列任务
console.log('nextTick in setImmediate');
});
});
/**
* 执行栈中为 nextTickQueue 添加任务
*/
process.nextTick(function () {
// 2. 执行栈为空,进入事件循环准备阶段,唤醒 prepare watcher,
// 检查 nextTickQueue,执行队列中的任务
console.log('nextTick');
setTimeout(function () {
// 5. 注册计时器任务,timers 阶段到达时间下限则执行该任务,
// 否则等到 poll 阶段
console.log('setTimeout in nextTick');
}, 0);
setImmediate(function () {
// 8. 注册 setImmediate 计时器,在当前循环的 check 阶段执行。
// (注:这是新的 ImmediateList,当前循环内有 2 个 ImmediateList 了)
console.log('setImmediate in nextTick');
});
process.nextTick(function () {
// 3. prepare watcher 处于活跃状态,检测 nextTickQueue 的新任务,
// 执行完所有任务后沉睡
console.log('nextTick in nextTick');
});
});
console.log('main thread'); // 1. 执行栈的任务
// 输出:
// main thread
// nextTick
// nextTick in nextTick
// setTimeout
// setTimeout in nextTick
// nextTick in setTimeout
// setImmediate
// setImmediate in nextTick
// setImmediate in setTimeout
// nextTick in setImmediate
// setTimeout in setTimeout
// setImmediate in setImmediate
//setTimeout in setImmediate
/* 后面 setImmediate 注册的回调会因为进程执行性能顺序有所不同 */