I am have a problem understanding why rejections are not passed on through a promise chain and I am hoping someone will be able to help me understand why. To me, attaching f
@Jordan firstly as commenters noted, when using deferred lib, your first example definitely produces result you expect:
promise1 rejected
promise2 rejected
promise3 rejected
Secondly, even if it would produce output you suggest, it wouldn't affect execution flow of your second snippet, which is a bit different, more like:
promise.then(function(first_value) {
console.log('promise1 resolved');
var promise = db.put(first_value);
promise.then(function (second_value) {
console.log('promise2 resolved');
var promise = db.put(second_value);
promise.then(
function (wins) { console.log('promise3 resolved'); },
function (err) { console.log('promise3 rejected'); return err; });
}, function (err) { console.log('promise2 rejected'); return err;});
}, function (err) { console.log('promise1 rejected'); return err});
and that, in case of first promise being rejected will just output:
promise1 rejected
However (getting to the most interesting part) even though deferred library definitely returns 3 x rejected
, most of other promise libraries will return 1 x rejected, 2 x resolved
(that leads to assumption you got those results by using some other promise library instead).
What's additionally confusing, those other libraries are more correct with their behavior. Let me explain.
In a sync world counterpart of "promise rejection" is throw
. So semantically, async deferred.reject(new Error())
in sync equals to throw new Error()
.
In your example you're not throwing errors in your sync callbacks, you just returning them, therefore you switch to success flow, with an error being a success value. To make sure rejection is passed further, you need to re-throw your errors:
function (err) { console.log('promise1 rejected'); throw err; });
So now question is, why do deferred library took returned error as rejection?
Reason for that, is that rejection in deferred works a bit different. In deferred lib the rule is: promise is rejected when it's resolved with an instance of error, so even if you do deferred.resolve(new Error())
it will act as deferred.reject(new Error())
, and if you try to do deferred.reject(notAnError)
it will throw an exception saying, that promise can be rejected only with instance of error. That makes clear why error returned from then
callback rejects the promise.
There is some valid reasoning behind deferred logic, but still it's not on par with how throw
works in JavaScript, and due to that this behavior is scheduled for change with version v0.7 of deferred.
Short summary:
To avoid confusion and unexpected results just follow the good practice rules:
Obeying to above, you'll get both consistent and expected results in both deferred and other popular promise libraries.