繁体   English   中英

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

[英]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

问题是可能的重复为什么在主模块中使用时,setTimeout(0)和setImmediate()的行为是否未定义?

要深入了解它们,请先完成事件循环阶段。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM