Javascript setImmediate vs.nextTick
Node.js版本0.10今天发布,并引入了Javascript setImmediate vs.nextTick,javascript,node.js,setimmediate,Javascript,Node.js,Setimmediate,Node.js版本0.10今天发布,并引入了setImmediate。文档建议在执行递归nextTick调用时使用它 从它看起来非常类似于进程.nextTick 如果要在事件队列中已存在的任何I/O事件回调之后对函数进行排队,我应该在何时使用下一个回调以及何时使用setImmediate?使用setImmediate。使用process.nextTick将函数有效地排在事件队列的最前面,以便在当前函数完成后立即执行 因此,如果您试图使用递归分解一个长时间运行、CPU受限的作业,现在,您可能希望使
setImmediate
。文档建议在执行递归nextTick
调用时使用它
从它看起来非常类似于进程.nextTick
如果要在事件队列中已存在的任何I/O事件回调之后对函数进行排队,我应该在何时使用下一个回调以及何时使用setImmediate?使用
setImmediate
。使用process.nextTick
将函数有效地排在事件队列的最前面,以便在当前函数完成后立即执行
因此,如果您试图使用递归分解一个长时间运行、CPU受限的作业,现在,您可能希望使用
setImmediate
而不是process.nextTick
对下一次迭代进行排队,否则任何I/O事件回调都无法在迭代之间运行。如果您希望在事件中已经存在的任何I/O事件回调之后对函数进行排队,请使用setImmediate
队列使用process.nextTick
将函数有效地排在事件队列的最前面,以便在当前函数完成后立即执行
因此,如果您试图使用递归分解一个长时间运行、CPU受限的作业,您现在可能希望使用
setImmediate
而不是process.nextTick
对下一次迭代进行排队,否则任何I/O事件回调都无法在两次迭代之间运行。在回答中的注释中,它没有明确说明nextTick从宏观语义转移到微观语义
在节点0.9之前(当引入setImmediate时),nextTick在下一个调用堆栈的开始处运行
从节点0.9开始,nextTick在现有调用堆栈的末尾运行,而setImmediate在下一个调用堆栈的开头
查看工具和详细信息在回答中的注释中,没有明确说明nextTick从宏观语义转换为微观语义 在节点0.9之前(当引入setImmediate时),nextTick在下一个调用堆栈的开始处运行 从节点0.9开始,nextTick在现有调用堆栈的末尾运行,而setImmediate在下一个调用堆栈的开头 查看工具和详细信息,如图所示
import fs from 'fs';
import http from 'http';
const options = {
host: 'www.stackoverflow.com',
port: 80,
path: '/index.html'
};
describe('deferredExecution', () => {
it('deferredExecution', (done) => {
console.log('Start');
setTimeout(() => console.log('TO1'), 0);
setImmediate(() => console.log('IM1'));
process.nextTick(() => console.log('NT1'));
setImmediate(() => console.log('IM2'));
process.nextTick(() => console.log('NT2'));
http.get(options, () => console.log('IO1'));
fs.readdir(process.cwd(), () => console.log('IO2'));
setImmediate(() => console.log('IM3'));
process.nextTick(() => console.log('NT3'));
setImmediate(() => console.log('IM4'));
fs.readdir(process.cwd(), () => console.log('IO3'));
console.log('Done');
setTimeout(done, 1500);
});
});
将给出以下输出
Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
我希望这有助于理解两者的区别
更新:
使用process.nextTick()
延迟的回调在任何其他I/O之前运行
事件被激发,而使用setImmediate()时,执行将排队
在队列中已存在的任何I/O事件之后
Node.js设计模式,由Mario Casciaro编写(可能是关于Node.js/js的最佳书籍)
举例说明
import fs from 'fs';
import http from 'http';
const options = {
host: 'www.stackoverflow.com',
port: 80,
path: '/index.html'
};
describe('deferredExecution', () => {
it('deferredExecution', (done) => {
console.log('Start');
setTimeout(() => console.log('TO1'), 0);
setImmediate(() => console.log('IM1'));
process.nextTick(() => console.log('NT1'));
setImmediate(() => console.log('IM2'));
process.nextTick(() => console.log('NT2'));
http.get(options, () => console.log('IO1'));
fs.readdir(process.cwd(), () => console.log('IO2'));
setImmediate(() => console.log('IM3'));
process.nextTick(() => console.log('NT3'));
setImmediate(() => console.log('IM4'));
fs.readdir(process.cwd(), () => console.log('IO3'));
console.log('Done');
setTimeout(done, 1500);
});
});
将给出以下输出
Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
我希望这有助于理解两者的区别
更新:
使用process.nextTick()
延迟的回调在任何其他I/O之前运行
事件被激发,而使用setImmediate()时,执行将排队
在队列中已存在的任何I/O事件之后
Node.js设计模式,由Mario Casciaro编写(可能是关于Node.js/js的最佳书籍)
我想我可以很好地说明这一点。由于在当前操作结束时调用了
nextTick
,因此递归调用它可能会阻止事件循环继续setImmediate
通过在事件循环的检查阶段触发来解决此问题,允许事件循环正常继续
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
假设我们刚刚运行了这个程序,正在逐步完成事件循环的第一次迭代。它将调用迭代为零的步骤
函数。然后它将注册两个处理程序,一个用于setImmediate
,另一个用于process.nextTick
。然后,我们从setImmediate
处理程序递归调用该函数,该处理程序将在下一个检查阶段运行。nextTick
处理程序将在中断事件循环的当前操作结束时运行,因此,即使它是第二次注册,它实际上也将首先运行
顺序是:nextTick
在当前操作结束时激发,下一个事件循环开始,正常事件循环阶段执行,setImmediate
激发并递归调用我们的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
现在,让我们将对step
的递归调用移到nextTick
处理程序中,而不是setImmediate
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);
现在,我们已经将对步骤
的递归调用移动到nextTick
处理程序中,事情将以不同的顺序运行。事件循环的第一次迭代运行并调用step
注册setimmdaite
处理程序以及nextTick
处理程序。当前操作结束后,我们的nextTick
处理程序将触发,该处理程序递归调用step
,并注册另一个setImmediate
处理程序以及另一个nextTick
处理程序。由于nextTick
处理程序在当前操作后激发,因此在nextTick
处理程序中注册nextTick
处理程序将导致第二个处理程序在当前处理程序操作完成后立即运行。nextTick
处理程序将继续启动,阻止当前事件循环继续。在看到单个setImmediate
处理程序触发之前,我们将完成所有nextTick
处理程序
上述代码的输出结果是:
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
调用将继续重复
Promise.resolve().then(() => { console.log('this happens FIRST'); });
setImmediate(() => {
console.log('this happens LAST');
})
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);
function foo(count, callback) {
if (count <= 0) {
return process.nextTick(() => callback(new TypeError('count > 0')));
}
myAsyncOperation(count, callback);
}