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 注册的回调会因为进程执行性能顺序有所不同 */