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

标签 javascript node.js electron

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

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

我也尝试过以下方法:

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

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

有什么性能更好的东西吗?

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

最佳答案

有两件事会比 setTimeout 运行得更快:

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

    The process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.

    This is not a simple alias to setTimeout(fn, 0). It is much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.

  • promise 结算通知

所以这些可能是您的工具带的工具,将其中一个或两个与 setTimeout 混合使用以实现您想要的平衡。

详细信息:

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

但实际上,现代实现中至少有两种任务队列:我们都想到的主要的(setTimeout 和事件处理程序放置任务的地方),以及“微任务”队列,其中某些异步操作被放置在主任务(或“宏任务”)的处理过程中。这些微任务会在宏任务完成后立即处理,主队列中的下一个宏任务之前——即使下一个宏任务在微任务之前排队。

nextTick 回调和 promise 结算通知都是微任务。因此,调度要么调度一个异步回调,要么调度一个将在下一个主要任务之前发生的回调。

我们可以在带有 setInterval 和 promise 解析链的浏览器中看到:

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 个微任务。还要注意我们编写的三个日志消息上的时间戳(它们不会出现在代码片段控制台中,直到宏任务显示它们,但时间戳会在它们被记录时显示给我们)。

所以基本上,您可以安排一堆微任务,并定期让这些微任务用完并运行下一个宏任务。


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

关于javascript - 非常快速的无限循环,不会阻塞 I/O,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42547070/

相关文章:

reactjs - 无法将状态数据作为 Prop 发送到另一个组件

JavaScript 作用域基础知识 : . 绑定(bind)还是 self =this?

javascript - 导入文件需要时间,代码失败后出现

javascript - react 如何将变量传递给获取请求

electron - electronic-builder无法在macOS上构建linux软件包-: Unknown target: build错误

electron - 如何在Windows 10中添加上一个,播放/停止,下一个最小化

javascript - Fine Uploader 如何传递表单数据?

javascript - (Ruby、Rails、Javascript)使用 javascript 窗口等管理嵌套模型...?

node.js - NodeJS 服务器端 Firebase 存储的安全外部链接

javascript - npx create-react-app 和 create-react-app 之间的区别