![](/img/trans.png)
[英]nodejs: setImmediate callback executed after setTimeout(fn, 0) inconsistent with the nodejs doc describes
[英]NodeJS - setTimeout(fn,0) vs setImmediate(fn)
这两者有什么区别,我什么时候会使用其中一个?
setTimeout就像在延迟完成后调用函数一样。 每当一个函数被调用时,它不会立即执行,而是排队,以便在所有正在执行和当前排队的事件处理程序首先完成后执行。 setTimeout(,0) 本质上意味着在当前队列中的所有当前函数被执行后执行。 无法保证需要多长时间。
setImmediate在这方面是类似的,只是它不使用函数队列。 它检查 I/O 事件处理程序的队列。 如果当前快照中的所有 I/O 事件都处理完毕,则执行回调。 它在最后一个 I/O 处理程序之后立即将它们排队,有点像 process.nextTick。 所以它更快。
此外 (setTimeout,0) 会很慢,因为它会在执行前至少检查一次计时器。 有时它可能会慢两倍。 这是一个基准。
var Suite = require('benchmark').Suite
var fs = require('fs')
var suite = new Suite
suite.add('deffered.resolve()', function(deferred) {
deferred.resolve()
}, {defer: true})
suite.add('setImmediate()', function(deferred) {
setImmediate(function() {
deferred.resolve()
})
}, {defer: true})
suite.add('setTimeout(,0)', function(deferred) {
setTimeout(function() {
deferred.resolve()
},0)
}, {defer: true})
suite
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').pluck('name'));
})
.run({async: true})
输出
deffered.resolve() x 993 ops/sec ±0.67% (22 runs sampled)
setImmediate() x 914 ops/sec ±2.48% (57 runs sampled)
setTimeout(,0) x 445 ops/sec ±2.79% (82 runs sampled)
第一个给出了最快的可能调用的想法。 您可以检查自己是否调用 setTimeout 的次数是其他次数的一半。 还要记住 setImmediate 会根据您的文件系统调用进行调整。 所以在负载下它的性能会降低。 我不认为 setTimeout 可以做得更好。
setTimeout 是一段时间后调用函数的非侵入式方法。 它就像在浏览器中一样。 它可能不适合服务器端(想想为什么我使用 benchmark.js 而不是 setTimeout)。
一篇关于事件循环如何工作并清除一些误解的好文章。 http://voidcanvas.com/setimmediate-vs-nexttick-vs-settimeout/
引用文章:
在 I/O 队列回调完成或超时后调用setImmediate
回调。 setImmediate 回调放在 Check Queue 中,在 I/O Queue 之后处理。
setTimeout(fn, 0)
回调放置在 Timer Queue 中,将在 I/O 回调以及 Check Queue 回调之后调用。 作为事件循环,每次迭代首先处理定时器队列,所以先执行哪个取决于事件循环是哪个阶段。
setImmediate()是在 I/O 事件回调之后和 setTimeout 和 setInterval 之前安排回调的立即执行。
setTimeout()是在延迟毫秒后安排一次性回调的执行。
文件上是这样说的。
setTimeout(function() {
console.log('setTimeout')
}, 0)
setImmediate(function() {
console.log('setImmediate')
})
如果你运行上面的代码,结果将是这样的......即使当前的文档指出“要在 I/O 事件回调之后和 setTimeout 和 setInterval 之前安排回调的“立即”执行。” ..
结果..
设置超时
立即设置
如果您将示例包装在另一个计时器中,它总是会打印 setImmediate,然后是 setTimeout。
setTimeout(function() {
setTimeout(function() {
console.log('setTimeout')
}, 0);
setImmediate(function() {
console.log('setImmediate')
});
}, 10);
总是使用setImmediate
,除非你真的确定你需要setTimeout(,0)
(但我什至无法想象,这是为了什么)。 setImmediate
回调几乎总是在setTimeout(,0)
之前执行,除非在第一个刻度和setImmediate
回调中调用。
setTimeout(fn,0) 可用于防止浏览器在大量更新时冻结。 例如在 websocket.onmessage 中,您可能有 html 更改,如果消息不断出现,使用 setImmidiate 时浏览器可能会冻结
我认为Navya S的答案不正确,这是我的测试代码:
let set = new Set();
function orderTest() {
let seq = [];
let add = () => set.add(seq.join());
setTimeout(function () {
setTimeout(function () {
seq.push('setTimeout');
if (seq.length === 2) add();
}, 0);
setImmediate(function () {
seq.push('setImmediate');
if (seq.length === 2) add();
});
}, 10);
}
// loop 100 times
for (let i = 0; i < 100; i++) {
orderTest();
}
setTimeout(() => {
// will print one or two items, it's random
for (item of set) {
console.log(item);
}
}, 100);
解释在这里
对提供的答案完全不满意。 我在这里发布了我认为更好的答案: https : //stackoverflow.com/a/56724489/5992714
要深入了解它们,请先完成事件循环阶段。
SetImmediate:它在“检查”阶段执行。 检查阶段在 I/O 阶段之后调用。
SetTimeOut:它在“计时器”阶段执行。 定时器阶段是第一个阶段,但在I/O阶段和检查阶段之后调用。
为了以确定性的方式获得输出,这将取决于事件循环处于哪个阶段; 因此,我们可以使用两个函数。
当 Javascript 引擎开始执行时,它会逐行检查代码。
setTimeout(function() {
console.log('setTimeout')
}, 0)
setImmediate(function() {
console.log('setImmediate')
})
到那个时刻
settimeout
,它从调用堆栈移动到调用队列并启动一个计时器来执行。
setimmediate
,它从调用堆栈移动到宏队列(在第一个循环完成后立即开始执行)
因此,如果settimeout
值为 0,它将在调用堆栈循环完成之前完成其计时器。
这就是为什么settimeout
会在setimmediate
之前打印。
现在,假设
setTimeout(function() {
setTimeout(function() {
console.log('setTimeout')
}, 0);
setImmediate(function() {
console.log('setImmediate')
});
}, 10);
这意味着,第一个主超时移动到调用队列。 同时,调用栈完成其执行。
所以,10ms后,function来到调用栈,直接执行setimmediate
。 因为调用堆栈已经可以自由执行任务了。
使用 setImmediate() 不阻塞事件循环。 一旦当前的事件循环完成,回调将在下一个事件循环中运行。
使用 setTimeout() 来控制延迟。 该函数将在指定的延迟后运行。 最小延迟为 1 毫秒。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.