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
Let’s call the promises returned by the
then
andcatch
methods by the numbers you have put in comments next to them. Note that thesethen
andcatch
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 ispromise
(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.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" (seejob
), 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".
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.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 athen
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.
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:
Nice to mention:
unhandled promise exception
that leads to a nodejs process crash