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)。
setImmediate
回调在I/O队列回调完成或超时后调用。 setImmediate回调放置在Check队列中,在I/O队列之后处理。
setTimeout(fn, 0)
回调放置在计时器队列中,并且将在I/O回调以及Check队列回调之后调用。由于事件循环在每个迭代中首先处理计时器队列,因此哪个先执行取决于事件循环处于哪个阶段。setImmediate() 用于在 I/O 事件回调函数执行完后、定时器之前立即执行 callback 函数。
setTimeout() 用于在 delay 毫秒后执行一次 callback 函数。
文档中是这么说的。
setTimeout(function() {
console.log('setTimeout')
}, 0)
setImmediate(function() {
console.log('setImmediate')
})
setTimeout(function() {
setTimeout(function() {
console.log('setTimeout')
}, 0);
setImmediate(function() {
console.log('setImmediate')
});
}, 10);
除非您确实需要 setTimeout(,0)
(但我甚至无法想象有什么用处),否则始终使用 setImmediate
。 setImmediate
回调几乎总是在 setTimeout(,0)
之前执行,除非在第一个 tick 中调用和 setImmediate
回调中。
setTimeout
应该是首选方法,只有在必要时才使用 setImmediate
。 - Rich Remer我认为 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更改,如果消息不断到来,则在使用setImmediate时浏览器可能会冻结。
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);
这意味着,首先主要超时移动到调用队列。同时,调用堆栈完成其执行。
因此,在10毫秒后,函数进入调用堆栈,它将直接执行setimmediate
。因为调用堆栈已经空闲以执行任务。
setImmediate
是宏任务而不是微任务。https://nodejs.dev/learn/understanding-setimmediate - Sergey使用setImmediate()来避免阻塞事件循环。回调函数将在下一个事件循环中运行,一旦当前事件循环完成。
使用setTimeout()来进行可控延迟。该函数将在指定的延迟时间后运行。最小延迟为1毫秒。
setImmediate
和nextTick
都是用于异步执行代码的函数。两者之间的区别在于它们被添加到事件循环队列的不同位置。setImmediate
将回调添加到下一个事件循环迭代中,在I/O事件之后执行,而nextTick
则在当前操作完成后立即执行,但在I/O事件之前。简言之,nextTick
总是在setImmediate
之前执行,因为它是在当前操作完成后立即执行,而setImmediate
要等待一个新的事件循环迭代。 - Benjamin Gruenbaum