Do settled promise chain handlers fire in the same turn?
In your example there are 7 promises.
p
Promise.resolve(console.log('A'))
- the promise returned by the first
then
call Promise.resolve(console.log('B'))
- the promise returned by the second
then
call - Promise.resolve(console.log('C'))
- the promise returned by the third
then
call
They are settled in that order. Their reactions are triggered in different turns. So, the console.log calls happen in different turns.
Even if there was a single promise with three reactions:
p.then(() => console.log('A'));
p.then(() => console.log('B'));
p.then(() => console.log('C'));
the reactions (the callbacks passed to the then
method) would still be
triggered in different turns.
tc39.github.io/ecma262/#sec-triggerpromisereactions
As you can see in that link, the reactions are iterated and there's a different job for each reaction.
On Sat, Feb 25, 2017 at 7:22 PM, /#!/JoePea <joe at trusktr.io> wrote:
I'm not sure what's the best way to phrase the subject (please advise on the terminology)
Re terminology: I think you mean, will they all fire in the same job? (More on jobs and job queues: tc39.github.io/ecma262/#sec-jobs-and-job- queues)
...but for example, if we have a promise chain
First, before we get to the question of "turns," just a note on that code:
Remember that the result of then
is always a promise. So writing:
.then(() => Promise.resolve(someValue))
is adding an extra promise with no benefit; just use:
.then(() => value)
and you'll still get a promise resolved with value
. So no need for those
Promise.resolve
calls (unless I'm missing a subtlety, which has been
known to happen).
const p = new Promise(r => setTimeout(r, 100)) p .then(() => Promise.resolve(console.log('A'))) .then(() => Promise.resolve(console.log('B'))) .then(() => Promise.resolve(console.log('C')))
, will the
console.log
s fire in the same turn when promisep
settles, or will they fire in three separate turns?
No, they're all queued as separate jobs -- but they're jobs that jump the
queue a bit (more on that in a minute). We can see this in FulfillPromise which calls
TriggerPromiseReactions which uses EnqueueJob to add each reaction, as a separate
job, to the "PromiseJobs"
queue.
Note that not all job queues are equal. In browsers, for instance, there
are macrotasks (or just tasks) and microtasks (where the JavaScript
spec uses the term "job," the HTML5 spec uses the term "task"). Macrotasks
are the big things you think of in browsers: DOM event handlers,
setTimeout
callbacks, that kind of thing. Microtasks are things like
promise resolutions: The microtasks scheduled by a macrotask run just after
the macrotask completes, before the next macrotask runs (even if the next
macrotask was added to the macrotask queue before the microtasks were added
to the microtask queue).
E.g., microtasks like promise completions get priority over macrotasks like
setTimeout
and DOM event handlers. That's part of why the spec
differentiates between the "PromiseJobs"
queue and the "ScriptJobs"
queue (tc39.github.io/ecma262/#table-26).
Here's an example (jsfiddle.net/s44h3wtv):
new Promise(resolve => {
setTimeout(resolve, 0);
setTimeout(() => console.log("Next"), 0);
})
.then(() => console.log("A"))
.then(() => console.log("B"))
.then(() => console.log("C"));
We're resolving in a macrotask, and the very next scheduled macrotask is to log "Next". And yet, the result of the above is:
A
B
C
Next
Note how the promise resolutions (microtasks) queued by the macrotask that
resolved the promise (the first setTimeout
) were performed before the
next macrotask (the setTimeout
logging "Next").
-- T.J.
Thanks T.J., that explains it well.
I asked because I'm working with synchronous tests, so I'm wondering how to resolve ES6 Promises with fake network data synchronously, and fire all the handlers synchronously, all within the synchronous test function.
Any recommendation on that?
On Sun, Feb 26, 2017 at 7:49 PM, /#!/JoePea <joe at trusktr.io> wrote:
Thanks T.J., that explains it well.
Glad that helped!
I asked because I'm working with synchronous tests, so I'm wondering how to resolve ES6 Promises with fake network data synchronously, and fire all the handlers synchronously, all within the synchronous test function.
Any recommendation on that?
Only way I know is to use an asynchronous test function instead. Mocha, Jasmine, etc., all have handling for asynchronous tests.
-- T.J.
I'm not sure what's the best way to phrase the subject (please advise on the terminology), but for example, if we have a promise chain
const p = new Promise(r => setTimeout(r, 100)) p .then(() => Promise.resolve(console.log('A'))) .then(() => Promise.resolve(console.log('B'))) .then(() => Promise.resolve(console.log('C')))
, will the
console.log
s fire in the same turn when promisep
settles, or will they fire in three separate turns?