How rsvp.js handles rejected promise with chain of failure callbacks
Asked Answered
P

1

6

Re: https://github.com/tildeio/rsvp.js

I have a function called doSomething() that does something for a little while and then returns an RSVP.Promise. Then, a chain of success and failure callbacks are registered with the returned promise (see code below). The behavior I expected was, if the promise is fulfilled, the chain of success callbacks registered with the promise will be fired, and if the promise is rejected (fails), the chain of failure callbacks will be fired.

I get the expected behavior for when the promise is fulfilled, but I get a behavior different than what I expected when the promise is rejected. That is, the success callbacks are chained and output of one success callback is passed on to the next success callback in the chain. But it appears the failure callbacks are not chained. They behave almost like the catch in a try/catch block (see code and output below).

Can someone explain this behavior? Is this really the way it's suppose to work, or is this an error in the way rsvp.js handles a rejected/failed promise that has a chain of failure callbacks registered with it? I'm reading the Promises/A+ spec now to try and figure this out, but if someone knows this stuff off the top of their head, would love to hear your explanation. Thanks in advance.

jsfiddle: http://jsfiddle.net/rylie/VYSj7/2/

doSomething()  // returns an RSVP.Promise object
    .then(
        function(message) { console.log("then success 1: " + message); return "from success 1"; },  // success callback
        function(message) { console.log("then failure 1: " + message); return "from failure 1"; }   // failure callback
    )
    .then(
        function(message) { console.log("then success 2: " + message); return "from success 2"; },  // success callback
        function(message) { console.log("then failure 2: " + message); return "from failure 2"; }   // failure callback
    )
    .then(
        function(message) { console.log("then success 3: " + message); return "from success 3"; }   // success callback
    )
    .then(
        null,
        function(message) { console.log("then failure 4: " + message); return "from failure 4"; }   // failure callback
    )
    .then(
        function(message) { console.log("then success 5: " + message); return "from success 5"; },  // success callback
        function(message) { console.log("then failure 5: " + message); return "from failure 5"; }   // failure callback
    );

** When the promise is fulfilled (succeeds), this is the output I get and expected:

then success 1: Promise fulfilled!
then success 2: from success 1
then success 3: from success 2
then success 5: from success 3 

** When the promise is rejected (fails), this is the output I get:

then failure 1: Promise rejected!
then success 2: from failure 1
then success 3: from success 2
then success 5: from success 3 

** This is what I expected (on a rejected/failed promise):

then failure 1: Promise rejected!
then failure 2: from failure 1
then failure 4: from failure 2
then failure 5: from failure 4    
Patrol answered 30/10, 2013 at 20:39 Comment(0)
Z
12

You should just forget that .then() even takes more than 1 argument and use the .catch method and it will make more sense.

Promises provide a correspondence to some sync code constructs but this isn't very visible when you just have a low-level .then(). What you are looking for is basically an array of callbacks/callback aggregation but that is not the point of promises at all.

Think of .catch(fn) same as .then(null, fn):

doSomething().then(function(val) {
    console.log(val);
}).catch(function(e) {
    console.error(e);
});

Parallels the sync code (imagine doSomething returns synchronously):

try {
    var val = doSomething();
    console.log(val);
}
catch(e) {
    console.error(e);
}

Multiple catches (remember that .catch is more readable alias to .then(null, fn)

doSomething().then(function(val) {
    console.log(val);
}).catch(function(e) {
    return e;
}).catch(function(e){
    //Will not get here ever
    //because error thrown from doSomething()
    //was handled in the upper catch which doesn't trow
});

Parallels:

try {
    try {
        var val = doSomething();
        console.log(val);
    }
    catch(e) {
        //no need for explicit return e
    }
}
catch( e ) {
    //Will not get here ever
    //because error thrown from doSomething()
    //was handled in the upper catch which doesn't trow
}

So now you should notice that you can create the expected result by throwing instead of returning http://jsfiddle.net/VYSj7/3/

.catch() is IMO an essential method for a promise library to provide and will also be included in the built-in Javascript promises in future. However, if such method isn't provided, you can (or should be, unfortunately there are implementations that don't use prototypes):

Promise.prototype.catch = function(fn) {
    return this.then(null, fn);
};

See also rejection turns into fulfillment

Zacharia answered 1/11, 2013 at 13:44 Comment(1)
Nice explanation, Esailija! Thanks.Patrol

© 2022 - 2024 — McMap. All rights reserved.