非常快速的无限循环,不会阻塞I / O.

时间:2017-03-02 04:46:31

标签: javascript node.js electron

对于不阻止I / O的无限循环,是否有更快的替代window.requestAnimationFrame()

我在循环中所做的与动画无关,所以我不在乎下一帧何时准备就绪,并且我已经读过window.requestAnimationFrame()受到显示器刷新率的限制或至少等待直到画出一帧。

我也尝试了以下内容:

function myLoop() {
    // stuff in loop
    setTimeout(myLoop, 4);
}

(4是因为这是setTimeout中的最小间隔,较小的值仍然默认为4.)但是,我需要更好的分辨率。

那里有更好的表现吗?

我基本上需要while(true)的非阻止版本。

2 个答案:

答案 0 :(得分:5)

setTimeout更早出现的两件事:

  • process.nextTick回调(NodeJS特定):

      

    process.nextTick()方法将回调添加到“下一个滴答队列”。一旦事件循环的当前转弯转到完成,将调用当前在下一个滴答队列中的所有回调。

         

    这不是setTimeout(fn, 0)的简单别名。效率更高。它在事件循环的后续滴答中触发任何其他I / O事件(包括定时器)之前运行。

  • 承诺结算通知

因此,这些可能是您工具带的工具,将setTimeout中的一个或两个混合使用,以达到您想要的平衡。

详细说明:

您可能知道,给定的JavaScript线程基于任务队列运行(规范将其称为作业队列);你可能知道,浏览器中有一个主要的默认UI线程,NodeJS运行一个线程。

但事实上,在现代实现中至少有两个任务队列:我们都想到的主要任务队列(其中setTimeout和事件处理程序放置他们的任务),以及“微任务”队列,其中某些异步操作在处理主任务(或“macrotask”)期间放置。一旦macrotask完成,就会处理这些微任务,主队列中的下一个macrotask之前 - 即使下一个macrotask在微任务之前排队。

nextTick回调和承诺结算通知都是微任务。因此,调度要么调度异步回调,要么在下一个主要任务之前进行调度。

我们可以在浏览器中看到setInterval和承诺解析链:

let counter = 0;

// setInterval schedules macrotasks
let timer = setInterval(() => {
  $("#ticker").text(++counter);
}, 100);

// Interrupt it
$("#hog").on("click", function() {
  let x = 300000;

  // Queue a single microtask at the start
  Promise.resolve().then(() => console.log(Date.now(), "Begin"));

  // `next` schedules a 300k microtasks (promise settlement
  // notifications), which jump ahead of the next task in the main
  // task queue; then we add one at the end to say we're done
  next().then(() => console.log(Date.now(), "End"));

  function next() {
    if (--x > 0) {
      if (x === 150000) {
        // In the middle; queue one in the middle
        Promise.resolve().then(function() {
          console.log(Date.now(), "Middle");
        });
      }
      return Promise.resolve().then(next);
    } else {
      return 0;
    }
  }
});

$("#stop").on("click", function() {
  clearInterval(timer);
});
<div id="ticker">&nbsp;</div>
<div><input id="stop" type="button" value="Stop"></div>
<div><input id="hog" type="button" value="Hog"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

当你运行它并点击 Hog 按钮时,请注意计数器显示如何冻结,然后再继续。这是因为在它之前安排了300,000个微型计划。另请注意我们编写的三条日志消息的时间戳(它们不会出现在代码段控制台中,直到macrotask显示它们,但时间戳显示我们何时记录它们。)

所以基本上,你可以安排一堆微任务,并定期让它们用完并运行下一个macrotask。

注意:我在代码段中使用了setInterval作为浏览器示例,但setInterval对于使用NodeJS的类似实验可能不是一个好的选择,因为NodeJS的setInterval与浏览器中的<div id="slider"></div> <input type="number" id="firstInput"> <input type="number" id="secondInput"> 略有不同,并且具有一些令人惊讶的时序特征。

答案 1 :(得分:0)

有些lib可以像cron任务一样工作,例如https://www.npmjs.com/package/node-cron

我认为使用cron应该更容易,更灵活。