首页 > 解决方案 > 在 JavaScript 中创建“运行循环”的最佳解决方案

问题描述

所以事实证明,while 循环会阻塞 JavaScript 中的事件循环,如果您希望应用程序响应外部/异步事件,这会阻止它成为创建运行循环的可行解决方案。

我的问题是,使用什么技术使“运行循环”成为无限/恒定但仍然具有高性能并且不会阻塞事件循环。

我进行了快速测试以查看whilesetTimeout(fn, 0)(浏览器)和setImmediate(fn)(node.js)的性能。

var x = 1000
var isNode = typeof window == 'undefined'

function a() {
  var start = (new Date()).getTime()
  var q = 0

  function next() {
    q++
    if (q < x) {
      if (isNode) {
        setImmediate(next)
      } else {
        setTimeout(next, 0)
      }
    } else {
      var end = (new Date).getTime()
      console.log('a', end - start)
    }
  }

  next()
}

function b() {
  var start = (new Date()).getTime()
  var q = 0

  while (q < x) {
    q++
  }

  var end = (new Date).getTime()
  console.log('b', end - start)
}

a()
b()

节点.js:

a = 20
b = 0

浏览器:

a = 5039
b = 0

有没有一种方法可以像在 JavaScript 中执行 while 循环一样高效地执行“无限 while 循环”。setImmediate比 快得多setTimeout,但仍然比基本的 while 循环慢很多。

标签: javascriptevent-loop

解决方案


您可以使用 async/await 在 while 循环中屈服于事件循环:

// This can only be logged when the event loop
// is yielded to in the infinite loop below.
// So, it can only ever be logged between `#1` and `#2` being logged;
// it can sometimes occur more than once, or not at all between `#1` and `#2`
const interval = setInterval( _ => console.log( '#1.5' ), 0 );

(async _ => {

  // Using a for loop so this demo doesn't run forever.
  // Can use an infinite loop instead
  for ( let i = 0; i < 150; i++ ) { // while( true ) {

    console.log( '#1 About to yield, ' );
    await new Promise( resolve => setTimeout( _ => resolve(), 0 ) ); // yield
    console.log( '#2 Done yielding (other callbacks may have run)' );

  }

  clearInterval( interval );

})();

在 node.js 中使用setImmediate而不是 setTimeout。不要使用process.nextTick.

使用您的计时器进行演示:

var start = (new Date()).getTime()
var q = 0, x = 1000;

;(async _ => {
  while (q < x) {
    await new Promise( resolve => setTimeout( _ => resolve(), 0 ) );
    q++
  }

  var end = (new Date).getTime()
  console.log('a', end - start)
})();


推荐阅读