嵌套的process.nextTick问题
假设我们有以下代码
function main() {
process.nextTick(() => {
main();
})
}
main();
setTimeout(() => {
console.log(2)
},0);
那么2会输出吗?答案留到最后揭晓,有兴趣的同学可以先思考一下。我们分析一下这个过程。我们首先看一下nextTick的实现。
function nextTick(callback) {
let args;
// 参数处理
switch (arguments.length) {
case 1: break;
case 2: args = [arguments[1]]; break;
case 3: args = [arguments[1], arguments[2]]; break;
case 4: args = [arguments[1], arguments[2], arguments[3]]; break;
default:
args = new Array(arguments.length - 1);
for (let i = 1; i < arguments.length; i++)
args[i - 1] = arguments[i];
}
// 第一个任务的话,开启tick处理逻辑,开启了说明有tick任务需要处理
if (queue.isEmpty())
setHasTickScheduled(true);
// 生成一个新的节点
const tickObject = {
[async_id_symbol]: asyncId,
[trigger_async_id_symbol]: triggerAsyncId,
callback,
args
};
// 插入队列
queue.push(tickObject);
}
我们看到nextTick的实现很简单,就是生成一个节点,保存一些上下文,然后插入队列中。接着我们看一下处理tick任务的逻辑。
function processTicksAndRejections() {
let tock;
do {
// 每次nextTick时就会往queue中插入一个节点
while (tock = queue.shift()) {
try {
// nextTick的第一个函数,即回调
const callback = tock.callback;
// 执行回调
if (tock.args === undefined) {
callback();
} else {
const args = tock.args;
switch (args.length) {
case 1: callback(args[0]); break;
case 2: callback(args[0], args[1]); break;
case 3: callback(args[0], args[1], args[2]); break;
case 4: callback(args[0], args[1], args[2], args[3]); break;
default: callback(...args);
}
}
}
}
} while (!queue.isEmpty() || processPromiseRejections());
}
至此,我们或许已经知道了答案。就是while那里的queue队列一直在移除和追加,导致了死循环。nodejs早期版本限制了nextTick的嵌套深度,后来去掉了( process: remove maxTickDepth (Trevor Norris))。如果这个死循环不是符合预期的(即执行队列的回调时,回调又往队列里追加节点,导致死循环),那么我们可以怎么解决这个问题?其实nodejs中有很多这种场景,也给了解决方案。我们看一下如何处理该问题。
static void uv__run_closing_handles(uv_loop_t* loop) {
uv_handle_t* p;
uv_handle_t* q;
// 先保存对队列的引用
p = loop->closing_handles;
// 重置头指针
loop->closing_handles = NULL;
while (p) {
q = p->next_closing;
uv__finish_close(p);
p = q;
}
}
uv__run_closing_handles是libuv close阶段的处理函数。我们看到nodejs中是如何处理这个问题的,首先保存对待处理队列的引用,然后重置头指针。接着遍历处理待处理的队列,这时候即使回调里追加节点,也只会追加到原来的队列中,而不是追加到待处理队列导致死循环。今天就分析到这,谢谢。