Search code examples
javascripttypescriptpromisees6-promise

Multiple chains of then on a JS Promise?


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

Solution

  • 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:

    • "then1" is added to the MT Stack
    • test() returns
    • "then2" is added to the MT Stack

    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...