1

I have the following code (http://jsfiddle.net/tj1eo86s/):

promise(1).then(function() {
    console.log("OK1");
    promise(2).then(function() {
        console.log("OK2");
        promise(3).then(function() {
            console.log("OK3");
        }, function() {
            console.log("KO3");
        });
    }, function() {
        console.log("KO2");
    });
}, function() {
    console.log("KO1");
});

If promise(2) is rejected, the output will be:

OK1
KO2

How can I obtain the same behavior while chaining the promises?

I tried with (http://jsfiddle.net/7goh0ds9/):

promise(1)
.then(function() {
    console.log("OK1");
    return promise(2);
}, function() {
    console.log("KO1");
})
.then(function() {
    console.log("OK2");
    return promise(3);
}, function() {
    console.log("KO2");
})
.then(function() {
    console.log("OK3");
}, function() {
    console.log("KO3");
});

But the output is:

OK1
KO2
OK3

Then I tried to throw an error in the error callbacks (http://jsfiddle.net/cyx6mohy/):

promise(1)
.then(function() {
    console.log("OK1");
    return promise(2);
}, function() {
    console.log("KO1");
    throw "KO1";
})
.then(function() {
    console.log("OK2");
    return promise(3);
}, function() {
    console.log("KO2");
    throw "KO2";
})
.then(function() {
    console.log("OK3");
}, function() {
    console.log("KO3");
    throw "KO3";
});

But now I have:

OK1
KO2
KO3

I actually understand all those behaviors, but I have not been able to find a solution to handle errors as if promises were nested.

sp00m
  • 47,968
  • 31
  • 142
  • 252
  • I don't quite understand what your question really is. Nesting is a different structure than chaining so it's hard to figure out how you make one structure behave identically to the other in concept. When you get a rejected promise, you completely control what happens next. If you handle the rejection, then whatever you return from the reject handler becomes the continuation value. If you throw or return a rejected promise from the reject handler, then the parent promise will be rejected and the next reject handler in the chain will get called. continued in next comment... – jfriend00 Sep 23 '15 at 16:54
  • If you handle the rejection and do not throw or return a rejected promise, then the next success handler in the chain will get called. By "handling" the rejection and not returning a continuation error, you have "handled" the error and normal success processing continues. – jfriend00 Sep 23 '15 at 16:55
  • @jfriend00 Yes, thank you for these explanations, that's what I read as well (that's why I said that I actually understand all those behaviors). The point is that I find chained promises more readable and maintainable than nested promises (especially when you have many of them), but nested promises actually behave the way I need. So the question is: when chaining promises, how to handle errors the same way as they are handled when the promises are nested. – sp00m Sep 23 '15 at 17:00
  • 1
    Well your question is a generic, "how to make chain work like nested" and the generic answer is you can't. They aren't the same structure. If your question is really how do I get this specific output when this error occurs while using this set of chained promises, then an answer that is entirely specific to your exact code situation might be offered. There is no generic answer. – jfriend00 Sep 23 '15 at 17:02
  • @jfriend00 That's what I was afraid of. But "you can't" is actually the right answer then, so maybe you could add it as an answer. – sp00m Sep 23 '15 at 17:14
  • Also have a look [here](http://stackoverflow.com/a/21578854/1048572), [there](http://stackoverflow.com/q/29387939/1048572) or [any of these](http://stackoverflow.com/search?tab=votes&q=%5bpromise%5d%20break%20chain) – Bergi Sep 26 '15 at 15:58

2 Answers2

2

If your question is how to I generically make chaining handle errors the exact same way as arbitrary nesting, the answer is that you can't in a generic way. Chaining is a fundamentally different control structure than arbitrary nesting.

You could design a nesting that had fundamentally the same behavior as chaining. Or, if you had a specific error handling behavior from a nested structure that you wanted to emulate in a chained world, you "might" be able to use the appropriate error handling and control flow in a chained sequence to make that happen. Note, I say "might" because it depends entirely upon what the nested sequence was doing. You also might not be able to emulate it with pure chaining.

If your question is how do I make this sequence of chained promises generate this specific response when this specific promise in the chain rejects, then that might be possible, but it would be coded for your specific circumstance and desired outcome, not in some generic way.

Basically, there are reasons to use chaining and there are reasons to use nesting and reasons to use a combination of the two. Like you, I prefer chaining because the control flow and readability just seems simpler, so I always start out with that structure in mind, but there are definitely times in my code where I have to use nesting for some part of the operation (often because of branching decisions or certain types of required error handling).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

You can reject with the index as argument, then add a .catch and handle the error there:

var breakOnPromise = 2;

function promise(i) {
    return new Promise(function(resolve, reject) {
        if (i == breakOnPromise) {
            reject(i); // pass index to catch
        } else {
            resolve();
        }
    });
}

promise(1).then(function() {
    console.log("OK1");
    return promise(2);
}).then(function() {
    console.log("OK2");
    return promise(3);
}).then(function() {
    console.log("OK3");
}).catch(function(i) {
    // This is just for your case,
    // but you could use an `if` statement
    var err = ["KO1", "KO2", "KO3"][i - 1];
    console.log(err);
});
elclanrs
  • 92,861
  • 21
  • 134
  • 171