Does this mean I can’t count on the callback supplied to
.then
being evaluated before a callback supplied tosetTimeout
in an otherwise synchronous control flow?
Yes, that’s what it means; the spec doesn’t require that implementations work that way.
But in practice, the implementations with native Promise
support I’ve tested it on have scheduled the then
callback (a “microtask” from the PendingJobs queue) immediately after finishing the “macrotask” that scheduled it, before other pending macrotasks, even when the pending macrotask was scheduled before the microtask. (setTimeout
and events are macrotasks.)
E.g., in the environments where I’ve tested it, this outputs A
, C
, B
reliably:
console.log("A");
setTimeout(_ => console.log("B"), 0);
Promise.resolve().then(_ => console.log("C"));
But the JavaScript spec doesn’t require it.
As Bergi points out, for user agent environments, the HTML5 spec covers this in its specification for microtasks and macrotasks. But that’s only applicable to user agent environments (like browsers).
Node doesn’t follow that spec’s definition, for instance (not least because its timer functions return objects, not numbers), but Node also gives us A
, C
, B
above, because (thanks Benjamin Gruenbaum!) it runs promise resolutions after the nextTick
queue but before any timer or I/O callbacks. See his gist for details.