12

I'm seeing this behavior in Node and Chrome:

setTimeout(()=>{ console.log('timeout') }, 0)
Promise.resolve().then(()=>{ console.log('promise') })
console.log('sync')

// output order:
// sync
// promise
// timeout

My question is, is this consistent behavior? I.e, according to spec, does a then or await on a memoized/already resolved promise always fire before setTimeout(fn, 0)?

I want to use this in something like the following, returning one thing if I have a memoized result in my promise and another if not:

// somewhere during object initialization
this.resultingPromise = expensiveAsyncFunction()

// in a method called frequently
Promise.race([
    new Promise(resolve => setTimeout(() => resolve('default'), 0)),
    this.resultingPromise
])
Jonas.z
  • 415
  • 4
  • 18
  • I'm gonna go out on a limb and say that logic suggests it will always work that way. I don't know the inner workings of either mechanism, but if you set up a timer, create a resolved promise and then output to the console I'd expect to see what you show. The console.log works immediately, the timer is then created and set to execute the callback, and the resolved promise is executed. Then, the timer callback is executed because that would be the next step - the timer countdown hits zero. That sounds sensible to me, and I trust JS to be sensible. – Reinstate Monica Cellio Jan 10 '19 at 09:27
  • 2
    This article by Jake Archibald might help clarify this behavior: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ – Arnelle Balane Jan 10 '19 at 09:29
  • 1
    based on https://stackoverflow.com/a/36877743/5378743 summary point 2 the you can't rely on an order. – Roland Starke Jan 10 '19 at 09:29
  • 1
    Thanks @ArnelleBalane, saw now you had posted roughly the same thing before kirill.buga did in their answer below. I think I should read up on micro-/macrotasks to understand this – Jonas.z Jan 10 '19 at 09:56

4 Answers4

15

Promise.resolve will schedule a microtask while setTimeout schedule a macrotask. And the microtasks will run before running the next macrotask.

More information about event loop in general: https://www.youtube.com/watch?v=8aGhZQkoFbQ

More technical details about events loop: https://www.youtube.com/watch?v=cCOL7MC4Pl0

kirill.buga
  • 1,129
  • 2
  • 12
  • 26
  • This is a bit enlighting, will check out the videos. Found this answer https://stackoverflow.com/a/25933985/1555158 suggesting this concept of micro-/macrotasks might be in the WHATWG standard. Is the WHATWG standard also part of es6 and the likes? Or am I getting this wrong, the guy only refers to WHATWG to point out that a `task queue` is in that standard – Jonas.z Jan 10 '19 at 09:44
  • Okay. going to accept this answer. From the @ArnelleBalane link above (https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/#what-are-some-browsers-doing-differently) it seems ECMAScript defines a notion of "jobs" rather than being explicit about what's actually microtasks and macrotasks. So even though implementations have this consistent behavior, I can't find anything stating that it _must_ always have the above execution order – Jonas.z Jan 10 '19 at 10:09
1

so you have 2 async waiting States, but notice that one of them is constant and one is changing (variable). The timeout is set in an XML variable aside while the promise could took forever. If I understood your question quite well, when you have something you rely on take too long and something too short, unless you have a constant applied on one of them like the timeout, then one might end up running shorter unexpectedly (!) Be prepared for that and instead use monolithic structure from code security reasons and not performance.

1

There's no guarantee that one will be before the other. If you want to guarantee the order of execution - use Promises.

Bwaxxlo
  • 1,860
  • 13
  • 18
-1

From my understanding Promise has a higher priority in the call stack than setTimeout, and of course synchronous code block will be the first to be executed. In that case, yes the observed behaviour above (in the order of synchronous code block, promise.resolve, and setTimeout 0) should be consistent.

Van
  • 636
  • 3
  • 14