Node.js 0.10版本今天发布,并引入了setimmediation。API更改文档建议在进行递归nextTick调用时使用它。
根据MDN的说法,它似乎与process.nextTick非常相似。
什么时候应该使用nextTick,什么时候应该使用setimmediation ?
Node.js 0.10版本今天发布,并引入了setimmediation。API更改文档建议在进行递归nextTick调用时使用它。
根据MDN的说法,它似乎与process.nextTick非常相似。
什么时候应该使用nextTick,什么时候应该使用setimmediation ?
当前回答
我想我可以很好地解释这个。由于nextTick是在当前操作结束时调用的,因此递归地调用它可能会阻止事件循环继续进行。setimmediation通过在事件循环的检查阶段触发来解决这个问题,允许事件循环正常继续。
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
来源:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
请注意,检查阶段紧跟在轮询阶段之后。这是因为轮询阶段和I/O回调是对setimmediation的调用最有可能运行的地方。理想情况下,大多数调用都是立即的,只是不像nextTick那样立即,nextTick在每次操作后都会检查,技术上存在于事件循环之外。
让我们看一个setimmediation和process.nextTick区别的小例子:
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
step(iteration + 1); // Recursive call from setImmediate handler.
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
});
}
step(0);
Let's say we just ran this program and are stepping through the first iteration of the event loop. It will call into the step function with iteration zero. It will then register two handlers, one for setImmediate and one for process.nextTick. We then recursively call this function from the setImmediate handler which will run in the next check phase. The nextTick handler will run at the end of the current operation interrupting the event loop, so even though it was registered second it will actually run first.
最后的顺序是:nextTick在当前操作结束时触发,下一个事件循环开始,正常的事件循环阶段执行,setimmediation触发并递归调用step函数重新开始整个过程。当前操作结束,nextTick触发,等等。
上述代码的输出将是:
nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9
现在让我们将递归调用移到nextTick处理程序中,而不是setimmediation。
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
step(iteration + 1); // Recursive call from nextTick handler.
});
}
step(0);
Now that we have moved the recursive call to step into the nextTick handler things will behave in a different order. Our first iteration of the event loop runs and calls step registering a setImmedaite handler as well as a nextTick handler. After the current operation ends our nextTick handler fires which recursively calls step and registers another setImmediate handler as well as another nextTick handler. Since a nextTick handler fires after the current operation, registering a nextTick handler within a nextTick handler will cause the second handler to run immediately after the current handler operation finishes. The nextTick handlers will keep firing, preventing the current event loop from ever continuing. We will get through all our nextTick handlers before we see a single setImmediate handler fire.
上面代码的输出结果是:
nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9
请注意,如果我们没有中断递归调用并在10次迭代后中止它,那么nextTick调用将继续递归,并且永远不会让事件循环继续到下一个阶段。这就是为什么递归使用nextTick会阻塞,而setimmediation会在下一个事件循环中触发,并且从一个事件循环中设置另一个setimmediation处理程序根本不会中断当前事件循环,允许它继续正常执行事件循环的各个阶段。
希望有帮助!
PS -我同意其他评论者的观点,这两个函数的名称可以很容易地交换,因为nextTick听起来像是要在下一个事件循环中触发,而不是当前循环的结束,而且当前循环的结束比下一个循环的开始更“直接”。哦,好吧,这就是我们得到的API成熟,人们开始依赖现有的接口。
其他回答
在回答的评论中,它没有明确地说明nextTick从Macrosemantics转移到Microsemantics。
在节点0.9之前(引入setimmediation时),nextTick在下一个调用堆栈的开始操作。
从节点0.9开始,nextTick操作在现有调用堆栈的末尾,而setimmediation操作在下一个调用堆栈的开始
查看https://github.com/YuzuJS/setImmediate获取工具和详细信息
我想我可以很好地解释这个。由于nextTick是在当前操作结束时调用的,因此递归地调用它可能会阻止事件循环继续进行。setimmediation通过在事件循环的检查阶段触发来解决这个问题,允许事件循环正常继续。
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
来源:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
请注意,检查阶段紧跟在轮询阶段之后。这是因为轮询阶段和I/O回调是对setimmediation的调用最有可能运行的地方。理想情况下,大多数调用都是立即的,只是不像nextTick那样立即,nextTick在每次操作后都会检查,技术上存在于事件循环之外。
让我们看一个setimmediation和process.nextTick区别的小例子:
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
step(iteration + 1); // Recursive call from setImmediate handler.
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
});
}
step(0);
Let's say we just ran this program and are stepping through the first iteration of the event loop. It will call into the step function with iteration zero. It will then register two handlers, one for setImmediate and one for process.nextTick. We then recursively call this function from the setImmediate handler which will run in the next check phase. The nextTick handler will run at the end of the current operation interrupting the event loop, so even though it was registered second it will actually run first.
最后的顺序是:nextTick在当前操作结束时触发,下一个事件循环开始,正常的事件循环阶段执行,setimmediation触发并递归调用step函数重新开始整个过程。当前操作结束,nextTick触发,等等。
上述代码的输出将是:
nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9
现在让我们将递归调用移到nextTick处理程序中,而不是setimmediation。
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
step(iteration + 1); // Recursive call from nextTick handler.
});
}
step(0);
Now that we have moved the recursive call to step into the nextTick handler things will behave in a different order. Our first iteration of the event loop runs and calls step registering a setImmedaite handler as well as a nextTick handler. After the current operation ends our nextTick handler fires which recursively calls step and registers another setImmediate handler as well as another nextTick handler. Since a nextTick handler fires after the current operation, registering a nextTick handler within a nextTick handler will cause the second handler to run immediately after the current handler operation finishes. The nextTick handlers will keep firing, preventing the current event loop from ever continuing. We will get through all our nextTick handlers before we see a single setImmediate handler fire.
上面代码的输出结果是:
nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9
请注意,如果我们没有中断递归调用并在10次迭代后中止它,那么nextTick调用将继续递归,并且永远不会让事件循环继续到下一个阶段。这就是为什么递归使用nextTick会阻塞,而setimmediation会在下一个事件循环中触发,并且从一个事件循环中设置另一个setimmediation处理程序根本不会中断当前事件循环,允许它继续正常执行事件循环的各个阶段。
希望有帮助!
PS -我同意其他评论者的观点,这两个函数的名称可以很容易地交换,因为nextTick听起来像是要在下一个事件循环中触发,而不是当前循环的结束,而且当前循环的结束比下一个循环的开始更“直接”。哦,好吧,这就是我们得到的API成熟,人们开始依赖现有的接口。
如果您希望将函数置于已经在事件队列中的任何I/O事件回调之后,请使用setimmediation。使用过程。nextTick可以有效地将函数排在事件队列的头部,以便在当前函数完成后立即执行。
因此,在试图使用递归分解长时间运行的cpu绑定作业的情况下,您现在会希望使用setimmediation而不是process。nextTick将在下一个迭代中排队,否则任何I/O事件回调都没有机会在迭代之间运行。
这里有一些很好的答案,详细介绍了它们是如何工作的。
只需添加一个回答特定问题的答案:
什么时候应该使用nextTick,什么时候应该使用setimmediation ?
总是使用setimmediation。
Node.js事件循环、定时器和process.nextTick()文档包括以下内容:
我们建议开发人员在所有情况下都使用setimmediation(),因为它更容易推理(并且它导致代码与更广泛的环境兼容,如浏览器JS)。
在文档的前面,它会警告这个过程。nextTick可以导致……
一些糟糕的情况,因为它允许您通过递归process.nextTick()调用“饿死”您的I/O,这可以防止事件循环到达轮询阶段。
事实证明,处理。nextTick甚至可以让你挨饿
Promise.resolve().then(() => { console.log('this happens LAST'); });
process.nextTick(() => {
console.log('all of these...');
process.nextTick(() => {
console.log('...happen before...');
process.nextTick(() => {
console.log('...the Promise ever...');
process.nextTick(() => {
console.log('...has a chance to resolve');
})
})
})
})
另一方面,setimmediation“更容易推理”,并避免了这些类型的问题:
Promise.resolve().then(() => { console.log('this happens FIRST'); });
setImmediate(() => {
console.log('this happens LAST');
})
因此,除非有特定的需要,流程的独特行为。nextTick,建议的方法是“在所有情况下使用setimmediation()”。
你永远不应该使用process中断cpu负荷大的操作。
你不应该使用process.nextTick()中断这样的工作。这样做将导致一个永远不会清空的微任务队列——您的应用程序将永远被困在同一个阶段! ——托马斯·亨特使用Node.js的分布式系统
事件循环的每个阶段包含几个回调:
一旦process.nextTick()被触发,它将始终保持在相同的阶段。
例子
const nt_recursive = () => process.nextTick(nt_recursive);
nt_recursive(); // setInterval will never run
const si_recursive = () => setImmediate(si_recursive);
si_recursive(); // setInterval will run
setInterval(() => console.log('hi'), 10);
在本例中,setInterval()表示应用程序执行的一些异步工作,例如响应传入的HTTP请求。
一旦nt_recursive()函数运行,应用程序就会得到一个永远不会清空的微任务队列,异步工作也永远不会得到处理。
但是替代版本si_recursive()没有同样的副作用。
在检查阶段中调用setimmediation()会将回调添加到下一个事件循环迭代的检查阶段队列,而不是当前阶段的队列。
Case可能使用process.nextTick
使函数处理都异步进行。
function foo(count, callback) {
if (count <= 0) {
return process.nextTick(() => callback(new TypeError('count > 0')));
}
myAsyncOperation(count, callback);
}
——托马斯·亨特使用Node.js的分布式系统
在这种情况下,使用setimmediation()或process.nextTick()都可以;只是要确保你不会意外地引入递归。