NodeJS-setTimeout (fn,0) vs setDirect (fn)

这两者之间的区别是什么? 什么时候我会使用其中一个而不是另一个?

55704 次浏览

SetInstate () 是在 I/O 事件回调之后、 setTimeout 和 setInterval 之前安排立即执行回调。

SetTimeout () 是在延迟毫秒之后安排一次性回调的执行。

文件上是这么写的。

setTimeout(function() {
console.log('setTimeout')
}, 0)


setImmediate(function() {
console.log('setImmediate')
})

如果运行上面的代码,结果将是这样的... ... 尽管当前的文档声明“要在 I/O 事件回调之后、 setTimeout 和 setInterval 之前安排“立即”执行回调。”..

结果..。

SetTimeout

谢谢

如果将示例包装在另一个计时器中,它将始终打印 setImatiate,然后打印 setTimeout。

setTimeout(function() {
setTimeout(function() {
console.log('setTimeout')
}, 0);
setImmediate(function() {
console.log('setImmediate')
});
}, 10);

always use setImmediate, unless you are really sure that you need setTimeout(,0) (but I can't even imagine, what for). setImmediate callback will almost always be executed before setTimeout(,0), except when called in first tick and in setImmediate callback.

SetTimeout 就像在延迟结束后调用函数一样简单。无论何时调用函数,它都不会立即执行,而是排队,以便在所有正在执行和当前正在排队的事件句柄先完成后再执行。SetTimeout (,0)实质上意味着在当前队列中的所有当前函数执行完毕之后执行。我们无法保证这需要多长时间。

除了不使用函数队列之外,setDirect 在这方面是类似的。它检查 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})

Output

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 的次数是其他调用的一半,您可以检查自己。还要记住,setIM 将调整到您的文件系统调用。因此,在负载下,它的性能会降低。我不认为 setTimeout 可以做得更好。

SetTimeout 是在一段时间后调用函数的非侵入式方法。就像在浏览器里一样。它可能不适合于服务器端(想想我为什么使用 benchmark.js 而不是 setTimeout)。

为了不阻塞事件循环,请使用 setDirect ()。一旦完成当前事件循环,回调将在下一个事件循环中运行。

使用 setTimeout ()控制延迟。该函数将在指定的延迟后运行。最小延迟为1毫秒。

一篇关于事件循环如何工作的精彩文章,并澄清了一些误解。 Http://voidcanvas.com/setimmediate-vs-nexttick-vs-settimeout/

Citing the article:

在 I/O Queue 回调完成或超时后调用 setImmediate回调。SetInstate 回调放置在 Check Queue 中,在 I/O Queue 之后处理。

setTimeout(fn, 0)回调放置在 Timer Queue 中,将在 I/O 回调和 Check Queue 回调之后调用。作为事件循环,在每次迭代中首先处理计时器队列,因此首先执行哪个取决于哪个阶段事件循环。

我认为 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);

解释是 给你

SetTimeout (fn,0)可用于防止浏览器在大规模更新中冻结。例如,在 websocket.onmessage 中,可能会有 html 更改,如果继续出现消息,使用 setImmidiate 时浏览器可能会冻结

要深入理解它们,请一次通过事件循环阶段。

立即: 它在“检查”阶段执行,在 I/O 阶段之后调用 检查完毕阶段。

SetTimeOut: 它在“定时器”阶段执行。计时器阶段是第一阶段,但是在 输入/输出阶段和 将军阶段之后调用。

为了以确定的方式获得输出,它将取决于事件循环的阶段; 因此,我们可以使用两个阶段中的函数。

当 Javascript 引擎开始执行时,它会逐行检查代码。

setTimeout(function() {
console.log('setTimeout')
}, 0)


setImmediate(function() {
console.log('setImmediate')
})

当事情发生的时候

settimeout,它从调用堆栈移动到调用队列并启动执行计时器。

setimmediate, it moves from call-stack to macro-queue(which is start execution immediately after the first loop is complete)

因此,如果 settimeout值为0,它将在调用堆栈循环完成之前完成其计时器。

这就是为什么 settimeout会在 setimmediate之前打印。

现在,假设

setTimeout(function() {
setTimeout(function() {
console.log('setTimeout')
}, 0);
setImmediate(function() {
console.log('setImmediate')
});
}, 10);

这意味着,第一个主超时移动到 call-queue。同时 call-stack 完成其执行。

因此,在10ms 之后,函数来调用堆栈,它将直接执行 setimmediate。因为调用堆栈已经可以自由地执行任务。