randy randy - 5 months ago 20
Javascript Question

break promise.all on catch

I have a promise implementation like the one on MDN:



var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, "three");
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, "four");
});
var p5 = new Promise((resolve, reject) => {
reject("reject");
});

Promise.all([p1, p2, p3, p4, p5]).then(value => {
console.log(value);
}, function(reason) {
console.log(reason)
});





This does fast error-catching:


Promise.all
is rejected if one of the elements is rejected and
Promise.all
fails fast: If you have four promises which resolve after a
timeout
, and one calls
reject
immediately, then
Promise.all
rejects immediately.


However, the functions inside a
promise
don't stop running. What I want, is that if one function does a
reject
, the other functions stop running (preventing useless messages to a user).

Example of a promise that keeps running after the reject:



var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, "three");
});
var p4 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Don\'t show this after a reject!');
resolve();
}, 4000);
});
var p5 = new Promise((resolve, reject) => {
reject("reject");
});

Promise.all([p1, p2, p3, p4, p5]).then(value => {
console.log(value);
}, function(reason) {
console.log(reason)
});




Answer

There's nothing general in promises that will do that, since stopping the other actions is action-specific, not generic. For instance, in your example, you have to keep the timer handle and then use clearTimeout; with an ajax request, you might need to do an abort call; with something else, it would be something else.

So you'll need to handle this in your real code in a way that's specific to your real code, using .catch on the promise returned by Promise.all (or the second arg to then as you have).

In the specific code in your question, it would look something like this (I've added output to the timers we don't cancel as well), but again it will vary depending on what you're cancelling:

var timersToReject = [];
var p1 = new Promise((resolve, reject) => {
    setTimeout(v => {
        console.log("resolving " + v);
        resolve(v);
    }, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
    setTimeout(v => {
        console.log("resolving " + v);
        resolve(v);
    }, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
    setTimeout(v => {
        console.log("resolving " + v);
        resolve(v);
    }, 3000, "three");
});
var p4 = new Promise((resolve, reject) => {
    timersToReject.push(setTimeout(() => {
        console.log('Don\'t show this after a reject!');
        resolve();
    }, 4000));
});
var p5 = new Promise((resolve, reject) => {
    reject("reject");
});

Promise.all([p1, p2, p3, p4, p5]).then(value => {
    console.log(value);
}, function(reason) {
    console.log(reason)
    timersToReject.forEach(t => clearTimeout(t));
});