Is there a faster way to yield to Javascript event loop than setTimeout(0)?

后端 未结 4 645
误落风尘
误落风尘 2020-12-10 18:43

I am trying to write a web worker that performs an interruptible computation. The only way to do that (other than Worker.terminate()) that I know is to periodic

4条回答
  •  情歌与酒
    2020-12-10 19:23

    Yes, the message queue will have higher importance than timeouts one, and will thus fire at higher frequency.

    You can bind to that queue quite easily with the MessageChannel API:

    let i = 0;
    let j = 0;
    const channel = new MessageChannel();
    channel.port1.onmessage = messageLoop;
    
    function messageLoop() {
      i++;
      // loop
      channel.port2.postMessage("");
    }
    function timeoutLoop() {
      j++;
      setTimeout( timeoutLoop );
    }
    
    messageLoop();
    timeoutLoop();
    
    // just to log
    requestAnimationFrame( display );
    function display() {
      log.textContent = "message: " + i + '\n' +
                        "timeout: " + j;
      requestAnimationFrame( display );
    }

    Now, you may also want to batch several rounds of the same operation per event loop.

    Here are a few reasons why this method works:

    • Per specs, setTimeout will get throttled to a minimum of 4ms after the 5th level of call, that is after the fifth iteration of OP's loop.
      Message events are not subject to this limitation.

    • Some browsers will make the task initiated by setTimeout have a lower priority, in some cases.
      Namely, Firefox does that at page loading, so that scripts calling setTimeout at this moment don't block other events ; they do even create a task queue just for that.
      Even if still un-specced, it seems that at least in Chrome, message events have a "user-visible" priority, which means some UI events could come first, but that's about it. (Tested this using the up-coming scheduler.postTask() API in Chrome)

    • Most modern browsers will throttle default timeouts when the page is not visible, and this may even apply for Workers.
      Message events are not subject to this limitation.

    • As found by OP, Chrome does set a minimum of 1ms even for the first 5 calls.


    But remember that if all these limitations have been put on setTimeout, it's because scheduling that many tasks at such a rate has a cost.

    Use this only in a Worker thread!

    Doing this in a Window context will throttle all the normal tasks the browser has to handle, but which they'll consider less important, like Network requests, Garbage Collection etc.
    Also, posting a new task means that the event loop has to run at high frequency and will never idle, which means more energy consumption.

提交回复
热议问题