Chained promises not passing on rejection

前端 未结 3 2015
挽巷
挽巷 2020-11-22 02:18

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

3条回答
  •  被撕碎了的回忆
    2020-11-22 02:36

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

    1. Always reject your promises with an error instances (follow rules of sync world, where throwing value that's not an error is considered a bad practice).
    2. Reject from sync callbacks by throwing errors (returning them doesn't guarantee rejection).

    Obeying to above, you'll get both consistent and expected results in both deferred and other popular promise libraries.

提交回复
热议问题