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

Joe*_*oey 6 javascript node.js electron

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

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

我也尝试了以下内容:

function myLoop() {
    // stuff in loop
    setTimeout(myLoop, 4);
}
Run Code Online (Sandbox Code Playgroud)

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

那里有更好的表现吗?

我基本上需要一个非阻塞版本while(true).

T.J*_*der 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);
});
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

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

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


注意:我已经setInterval在代码段中使用了浏览器示例,但是setInterval,具体来说,对于使用NodeJS的类似实验可能不是一个好的选择,因为NodeJS setInterval与浏览器中的有点不同,并且具有一些令人惊讶的时序特征.