2

I am trying to understand the Fail-fast behavior of Promises.all. Consider the below example;

let p1 = new Promise(function(resolve, reject) {
    setTimeout(
        function(){
            console.log("p1");
            resolve('p1');
        }, 500);
});

let p2 = new Promise(function(resolve, reject) {
    setTimeout(
        function(){
            console.log("p2");
            resolve('p2');
        }, 1000);    
});

let p3 = new Promise(function(resolve, reject) {
    setTimeout(
        function(){
            console.log("p3");
            resolve('p3');
        }, 1200);
});

let p4 = new Promise(function(resolve, reject) {
    setTimeout(
        function(){
            console.log("p4");
            reject('p4');
        }, 600);
});

let p5 = new Promise(function(resolve, reject) {
    setTimeout(
        function(){
            console.log("p5");
            resolve('p5');
        }, 800);
});

let promise = Promise.all([p1, p2, p3, p4, p5]);

promise

.then(function(data) {
    data.forEach(function(data) {
        cconsole.log(data);
    });
})

.catch(function(error) {
    console.error('error is', error);
});

Running the above example does log p1, p2, p3, p4, p5 and then logs "error is p4"

Now, what I read about Promise.all is this;

Promise.all has a fail-fast behaviour. If a given promise is rejected, the resulting promise of Promise.all will be rejected at this exact moment. It will not wait for the other promises to complete

So my question is why/how does it log p1, p2, p3, p4, p5

I was thinking it should have just logged p1 (since that is less than 600 ms at which we have a reject) and then just log "error is p4"

Where might my understanding be wrong?

halfer
  • 19,824
  • 17
  • 99
  • 186
copenndthagen
  • 49,230
  • 102
  • 290
  • 442
  • 8
    The `console.log()` calls happen immediately, not after the timeouts. – Pointy Feb 23 '19 at 14:20
  • I updated the example a bit...Still see the same output – copenndthagen Feb 23 '19 at 14:31
  • 1
    Your update *significantly* changes the question, but it's still the case that the `console.log()` calls happen without regard to the operation of the Promise mechanism. The timeouts are not cancelled when one of the callbacks fails. – Pointy Feb 23 '19 at 14:33
  • If one rejected Promise would cancel all other promises, the other promises would be cancelled at a non-deterministic point, and that would be very bad, take for example `Promise.all([ notifyUser(), storeToDB() ])` if notifying the user fails and that would cancel the db operation you might corrupt the database. Instead of actively cancelling those other actions you should split up those actions into multiple promising functions, that you then call in chunks, e.g. `Promise.all([notifyUser(), prepareDB()]).then(() => Promise.all([storeDB(), sthElse()]));` – Jonas Wilms Feb 23 '19 at 14:39

1 Answers1

4

I think the fail-fast behaviour would be better illustrated by

function logFulfilled(val) {
   console.log(val + " fulfilled");
   return val;
}
function timeout(callback, delay, val) {
    console.log(val + " created");
    setTimeout(callback, delay, val);
}
let p1 = new Promise(function(resolve, reject) {
    timeout(resolve, 500, 'p1');
}).then(logFulfilled);

let p2 = new Promise(function(resolve, reject) {
    timeout(resolve, 1000, 'p2');
}).then(logFulfilled);

let p3 = new Promise(function(resolve, reject) {
    timeout(resolve, 1200, 'p3');
}).then(logFulfilled);

let p4 = new Promise(function(resolve, reject) {
    timeout(reject, 600, 'p4');
}).catch(err => {
   console.log(err + " rejected");
   throw err;
});

let p5 = new Promise(function(resolve, reject) {
    timeout(resolve, 800, 'p5');
}).then(logFulfilled);

let promise = Promise.all([p1, p2, p3, p4, p5]);
promise.then(console.log, console.error);

Notice that you get the Error log right after p4 got rejected, before p5, p2 and p3 are fulfilled.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks. But it does continue logging p5, p2, p3 fulfilled even after p4 is rejected – copenndthagen Feb 23 '19 at 14:35
  • 2
    @testndtv Yes, because promises cannot stop the underlying action - the `setTimeout` continues to run, it's not getting cancelled. The point is that `promise` is getting rejected immediately, that's all what "`Promise.all` fails fast" means. – Bergi Feb 23 '19 at 14:36
  • 5
    @testndtv fail-fast means that the Promise returned by `Promise.all` will rejected as soon as one of the pass Promises is rejected, without waiting for the others to finish. It does not mean that the other Promises are aborted, they still will finish their actions. – t.niese Feb 23 '19 at 14:37