Optimal solution to creating a “run loop” in JavaScript

那年仲夏 提交于 2019-12-06 14:26:11

问题


So it turns out the while loop blocks the event loop in JavaScript, which prevents it from being a viable solution to creating a run loop if you want your application to respond to external / async events.

My question is, what techniques are used to have a "run loop" be infinite / constant but still have high performance and not block the event loop.

I ran a quick test to see the performance of while, setTimeout(fn, 0) (browser), and 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()

node.js:

a = 20
b = 0

browser:

a = 5039
b = 0

Is there not a way to do an "infinite while loop" that is as performant as doing a while loop in JavaScript. setImmediate is much faster than setTimeout, but it's still a lot slower than the basic while loop.


回答1:


You can yield to the event loop inside a while loop with async/await:

// 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 );

})();

In node.js use setImmediate instead of setTimeout. Do not use process.nextTick.

To demo with your timer:

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)
})();



回答2:


Something like this will work:

var THRESHOLDCOUNT = 10000
var THRESHOLDTIME = 15

function loop(fn) {
  var start = Date.now()
  var changed = false
  var x = 0

  while (!changed) {
    while (x < THRESHOLDCOUNT) {
      x++
      fn()
    }

    var end = Date.now()
    changed = end - start > THRESHOLDTIME
  }

  // make room for external events.
  setImmediate(loop)
}



回答3:


If you really need a performant loop, I'd say go with requestAnimationFrame(), it's highly optimized and pretty easy to use.

https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame



来源:https://stackoverflow.com/questions/50011032/optimal-solution-to-creating-a-run-loop-in-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!