Reputation: 5686
Today I tried out what happens when I add multiple chains of then to a Promise. My test code looks like that (TypeScript)
class Test {
private simulateHeavyStuff(time: number){
// console.log("-"+time+"-");
var start = new Date().getTime();
var end = start;
while(end < start + time) {
end = new Date().getTime();
}
}
test(): Promise<void> {
let promise = new Promise<void>((resolve, reject) => {
resolve();
});
// ##### ADDING CHAIN 1 #####
promise
.then(()=>console.log("then1"))
.then(()=>this.simulateHeavyStuff(2000))
.then(()=>console.log("then11"))
.catch(console.error)
return promise;
}
}
let t = new Test();
// ##### ADDING CHAIN 2 #####
t.test()
.then(()=>console.log("then2"))
.then(()=>console.log("then21"))
.catch(console.error)
This prints out:
then1
then2
<---it's pausing here
then21
then11
Can someone explain the determinism behind this behavior?
I thought it would either print (parallel chains)
then1
then2
then21
<--- I guessed the pause will be here
then11
or (one chain)
then1
<--- with the pause here
then11
then2
then21
Upvotes: 1
Views: 117
Reputation: 5686
OK, I guess I understand what happens after some clarifications of Jaromanda X down in the comments!
It is fully deterministic!
Promises add the callbacks to the so called "Microtasks" stack. Other than other JS stacks those Microtasks run when the JS stack is empty (all synchronous code is done)...see the Video posted by Jaromanda X in the comments!
So it happens like this:
Synchronous code is done! Microtasks time!
The MT Stack looks like this now
then1
then2
So then1 is run and adds another Microtask to the MT stack as it returns a Promise. The MT stack looks like this now
then2
heavyStuff(2000)
As further Microtasks are run until the MT stack is empty this goes on until all MTs are finished. Then any other code goes on...
Upvotes: 2
Reputation: 5075
The answer seems to be that you—probably by mistake—created two promises (since .then
creates a new promise object based on the previous one, see MDN). Have a look at the comments below:
test(): Promise<void> {
// this creates the base promise "A"
let promise = new Promise((resolve, reject) => {
resolve();
});
// this creates promise "A1" by adding a chain of operations to "A"
// you are not returning it however
promise
.then(()=>console.log("then11"))
.then(()=>console.log("then12"))
.then(()=>this.simulateHeavyStuff(2000))
.then(()=>console.log("then14"))
.catch(console.error)
// this returns "A" (not "A1")
return promise;
}
// this creates promise "A2"
t.test()
.then(()=>console.log("then2"))
.then(()=>console.log("then21"))
.catch(console.error)
When you run the code snippet you will see that the processing of both promises seems to be done fully deterministically by the round-robin principle (meaning that the promises are executed in turn for every .then
operation step). The sequence is
"A1" -> "A2" -> "A1" ...
class Test {
simulateHeavyStuff(time){
// console.log("-"+time+"-");
var start = new Date().getTime();
var end = start;
while(end < start + time) {
end = new Date().getTime();
}
console.log('then13 (heavy stuff)');
}
test() {
let promise = new Promise((resolve, reject) => {
resolve();
});
// ##### ADDING CHAIN 1 #####
promise
.then(()=>console.log("then11"))
.then(()=>console.log("then12"))
.then(()=>this.simulateHeavyStuff(2000))
.then(()=>console.log("then14"))
.catch(console.error)
return promise;
}
}
let t = new Test();
// ##### ADDING CHAIN 2 #####
t.test()
.then(()=>console.log('then21'))
.then(()=>console.log('then22'))
.then(()=>console.log('then23'))
.catch(console.error)
In your case—where the execution steps are all fully synchronous—the program is being executed deterministically: i.e. the i-th chained .then
operation of promise "A1" precedes the i-th chained .then
operation of "A2"; and the i+1 chained .then
operation of promise "A1" follows the i-th chained .then
operation of promise "A2".
The ECMA Script 2015 specification seems to confirm this behavior since the .then
pools are supposed to queued in a job queue.
Upvotes: -1
Reputation: 23277
This is because your method test
returns promise
variable, that is not the same as result returned by catch
in it, so when you call .test()
, it will not wait the whole chain inside test
.
If you change
promise
.then(()=>console.log("then1"))
.then(()=>this.simulateHeavyStuff(2000))
.then(()=>console.log("then11"))
.catch(console.error)
to
promise = promise
.then(()=>console.log("then1"))
.then(()=>this.simulateHeavyStuff(2000))
.then(()=>console.log("then11"))
.catch(console.error)
it will work as you expected.
Upvotes: 0