question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Promises executor dominates over timeouts

See original GitHub issue

First, some table-setting:

var canceled = false;

const loop = () => {
  if (!canceled) {
    console.log("looping");
    new Promise((resolve, reject) => loop());
  }
};

setTimeout(() => { console.log("canceling"); canceled = true; }, 5);
loop();

This does what you would expect: it loops for a while and then cancels.

So does the following:

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.scalajs.concurrent.QueueExecutionContext
import scala.scalajs.js.timers

object Test {
  def main(args: Array[String]): Unit = {
    var cancel = false
    val exec: ExecutionContext = QueueExecutionContext.timeouts()
    def loop(): Unit = {
      if (!cancel) {
        println("looping")
        exec.execute(() => loop())
      }
    }

    timers.setTimeout(5.millis) { println("canceling"); cancel = true }
    loop()
  }
}

Again, loops for a little while and then cancels.

This, however, loops forever:

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.scalajs.concurrent.QueueExecutionContext
import scala.scalajs.js.timers

object Test {
  def main(args: Array[String]): Unit = {
    var cancel = false
    val exec: ExecutionContext = QueueExecutionContext.promises()        // <---- this is the only difference!
    def loop(): Unit = {
      if (!cancel) {
        println("looping")
        exec.execute(() => loop())
      }
    }

    timers.setTimeout(5.millis) { println("canceling"); cancel = true }
    loop()
  }
}

It appears that if any outstanding work is available on the promises queue, it will dominate over the timeouts queue. My guess without looking at the code is that this is a consequence of a well-intentioned optimization within ScalaJS itself which fails to yield, since it definitely doesn’t reproduce within Node.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:49 (27 by maintainers)

github_iconTop GitHub Comments

3reactions
djspiewakcommented, Sep 6, 2021

The above project now contains the polyfill executor implementation, as well as almost all the associated testing and CI infrastructure. The only thing it’s missing at present is the infrastructure for testing under webworker environments, which we’re working on.

Incidentally, doing this uncovered the fact that the polyfill is not working on jsdom. It doesn’t fail anything, but it falls back to setTimeout (with the associated clamping). We’ll see if that’s fixable (seems doable, since postMessage should be present in that environment).

Plan of record here is the following: https://github.com/typelevel/cats-effect/issues/2314

3reactions
gzm0commented, Aug 28, 2021

I created https://github.com/scala-js/scala-js-macrotask-executor and added you as a maintainer.

I’ll leave it up to @sjrd how to organize permissions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why Promises Are Faster Than setTimeout()? - Dmitri Pavlutin
The experiment has demonstrated that an immediately resolved promise is processed before an immediate timeout. The big question is... why? 2.
Read more >
Execution order of Timeout and Promise functions(Main Tasks ...
I think output one and four are pretty clear. setTimeout is a part of Main Task queue while Promise is of Micro task...
Read more >
SetTimeout vs Promises - Medium
I'll make a step by step analysis of the code execution. 1. The call stack executes setTimeout(..., 0) and schedules a timer. timeout()...
Read more >
Promise.all() - JavaScript - MDN Web Docs
Promise.all is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout...
Read more >
In what situation should I use a promise over a timeout ... - Quora
You should never use timeout instead of a promise, they do not serve the same purpose. Promise allows you to associate handlers with...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found