0

In a Node.js App with Q Promise, I have this code and i want to see this result:

a
b
end

but i see this:

end
a
b

this is my code:

var a =  function(){
        return q.Promise(function(resolve, reject){
            resolve("a");
        });
    };

    var b =  function(){
        return q.Promise(function(resolve, reject){
            resolve("b");
        });
    };

    var c =  function(){
        return q.Promise(function(resolve, reject){
            resolve("c");
        });
    };

    var master =  q();

    master.then(function(){
        return a();
    })

    .then(function(res){
        console.error(res);
        return b();
    })

    .then(function(res){
        console.error(res);
            return c();
    });

    master.then(function(){
        console.error("end");
    });

if i change this section:

master.then(function(){
        return a();
    })

to this:

master = master.then(function(){
        return a();
    })

Code works fine but i don't know why? this is important to me to know and understand this. can someone explain this to me?

Fcoder
  • 9,066
  • 17
  • 63
  • 100

1 Answers1

2

What you are seeing is the difference between chaining and branching which are different forms of promise control flow.

When you do:

master.then(function(){
    return a();
})

And, then later:

master.then(function(){
    console.error("end");
});

You have hooked two .then() handlers to the same promise. This is branching. When master() is resolved, each of these .then() handlers will be called independent of each other and each becomes their own independent chain (thus the original chain was branched into two chains).

Chaining would be like:

master.then(function(){
    return a();
}).then(function(){
    console.error("end");
});

In the chaining case, the second .then() handler will not be called until any promise returned by a() is also resolved and if a() throws or returns a rejected promise the second .then() handler here won't be called at all.

In the branching case in the first example, the second .then() handler is called right after the first .then() handler regardless of what a() returns.

When you then do:

master = master.then(function(){
    return a();
})

before you do:

master.then(function(){
    console.error("end");
});

You are manually chaining them such that you have effectively done:

master.then(function(){
    return a();
}).then(function(){
    console.error("end");
});

Remember that each call to .then() returns a new promise. Chaining, as in:

a().then(b).then(c)

creates a new new promise at each step in the chain and that new promise is not resolved until whatever the handler returns is also resolved.

So, when you're doing:

master = master.then(function(){
    return a();
})

You are grabbing that intermediate promise (by assigning to master) and hanging onto it so you can then chain something onto it. If you just do:

master.then(function(){
    return a();
})

Then, the promise that was returned from master.then() is done and cannot be chained onto directly.


See these posts for further description of chaining vs. branching:

Understanding javascript promises; stacks and chaining

Is there a difference between promise.then.then vs promise.then; promise.then


Here's a snippet that shows promise branching:

function log(str) {
    var d = document.createElement("div");
    d.textContent = str;
    document.body.appendChild(d);
}

function setDelay(t, msg) {
    return function() {
        return delay(t, msg);
    }
}

function delay(t, msg) {
    // if only one string argument, default t to 500ms
    if (typeof t === "string" && typeof msg === "undefined") {
        msg = t;
        t = 500;
    }
    return new Promise(function(resolve) {
        setTimeout(function() {
            log(msg);
            resolve(msg);
        }, t);
    });
}

var x = Promise.resolve().then(setDelay("One"))
 .then(setDelay("Two"))
 .then(function() {
     log("Three");
 });

x.then(setDelay(500, "Four"))
    .then(setDelay(500, "Five"))
    .then(setDelay(500, "Six"));

x.then(setDelay(50, "Seven"))
    .then(setDelay(50, "Eight"))
    .then(setDelay(50, "Nine"));

x.then(setDelay(10, "Ten"))
    .then(setDelay(10, "Eleven"));

Explanation

One, Two, Three are chained together and all the branches are dependent upon them. Then, that chain splits into three separate branches.

The Ten, Eleven branch executes first because it has 10ms timers.

Then, the Seven, Eight, Nine branch goes next with 50ms timers.

Then, the Four, Five, Six branch goes last with 500ms timers

Note: I've purposely made it so the branch timers don't overlap, but that is not something that the promises enforce, that just happens to be the case here because of the timer values I chose. All three branches are running indepdendently and could interleave if their activities had a timing that overlapped.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979