skip to Main Content

I am unable to understand the result of the below promise chain

function job(state) {
    return new Promise(function(resolve, reject) {
        if (state) {
            resolve('success');
        } else {
            reject('error');
        }
    });
}

let promise = job(true);

promise

.then(function(data) {              ///1
    console.log(data);

    return job(true);
})

.then(function(data) {              ///2
    if (data !== 'victory') {
        throw 'Defeat';
    }

    return job(true);
})

.then(function(data) {              ///3
    console.log(data);
})

.catch(function(error) {              ///4
    console.log(error);

    return job(false);
})

.then(function(data) {              ///5
    console.log(data);

    return job(true);
})

.catch(function(error) {              ///6
    console.log(error);

    return 'Error caught';
})

.then(function(data) {              ///7
    console.log(data);

    return new Error('test');
})

.then(function(data) {              ///8
    console.log('Success:', data.message);
})

.catch(function(data) {              ///9
    console.log('Error:', data.message);
});

The output is

success
Defeat
error
Error caught
Success: test

I can see why ‘success’ and ‘Defeat’ are printed.
But then I am confused. Why 3, 4,5 are skipped and it executes 6?
Then since 7 throws an error, shouldn’t 9 be executed instead of 8?

2

Answers


  1. Let’s call the promises returned by the then and catch methods by the numbers you have put in comments next to them. Note that these then and catch methods execute synchronously, so we immediately have a bunch of promises, and they are all pending at first. The only one that is immediately fulfilled is promise (which didn’t get a number, so let’s give it number 0).

    Now let’s look at this step by step:

    Promise 0 (promise) gets assigned a promise that fulfilled with value "success".

    Callback 1 executes, because promise 0 fulfilled, and so we get output "success". This callback returns a new promise that also fulfils with value "success". Promise 1 copies the state of that promise, and so it fulfils with "success".

    Callback 2 executes too, because promise 1 fulfilled. You don’t print anything in the callback number 2, but if you would, it would have been output. The if condition is true, and so callback 2 throws an error "Defeat". This makes that promise 2 rejects with "Defeat" as reason.

    Why 3, 4,5 are skipped

    The third callback is skipped, because that one is only supposed to execute when promise 2 fulfils. But as it rejected, and you didn’t provide a catch callback for it (second argument of then is absent), promise 3 just copies the state of promise 2. So now promise 3 is rejected too.

    Callback number 4 is not skipped; it executes! Promise number 3 gets rejected, and it is there that "Defeat" is printed. This callback then proceeds to return a rejected promise (job(false)) having reason "error" (see job), and so promise 4 also rejects with the same reason.

    Callback 5 is only supposed to execute when promise 4 fulfils, but as promise 4 rejected, it does not execute. Here also there is no callback to execute when promise 4 rejects, so promise 5 just copies the state of promise 4 and rejects with reason "error".

    and it executes 6?

    Callback 6 executes, because it is supposed to execute when promise 5 rejects — which it did — and so we get an output "error". This callback returns a string "Error caught", which becomes the value with which promise 6 fulfils.

    Callback 7 executes, because it is supposed to execute when promise 6 fulfils — which it did — and so we get an output "Error caught". Callback 7 returns an Error object with message "test". Promise 7 fulfils with this Error object as value.

    Then since 7 throws an error, shouldn’t 9 be executed instead of 8?

    That is a misunderstanding. Callback 7 does not throw an error. It returns an object, which happens to be an instance of Error, but that is irrelevant. If a then callback returns a value that is not a promise object (or another "thenable"), then that value will be what the corresponding promise fulfils with. Error objects are no exception to that rule.

    So callback 8 executes, because it is supposed to execute when promise 7 fulfils — which it did — and so we get an output "Error caught". Nothing is returned here, which means promise 8 fulfils with the value undefined.

    Callback 9 does not execute, because it is supposed to execute when promise 8 rejects, but it fulfilled. As there is no handler for when promise 8 fulfilled, promise 9 just copies the state of promise 8. As there is no callback attached to promise 9, nothing more gets executed.

    I hope this clarifies it.

    Login or Signup to reply.
  2. While the trincot explanation is very well detailed, what helped me to undestand the promise chain, is to visualize it.

    Here is what is happening in your code example:

    • every promise has a success or a failure
    • the dotted path represents the failure
    • the solid path represents the success
    • the colored path represents the code example execution

    Nice to mention:

    • if the dashed paths are not handled by a catch, it generates an unhandled promise exception that leads to a nodejs process crash
    • if a promise returns a promise (or a promise chain), it is inserted into the oriented graph that is being created

    promise chain

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search