Skip to content

Instantly share code, notes, and snippets.

@SupaFuzz
Created August 18, 2022 20:11
Show Gist options
  • Save SupaFuzz/d9e51c512d75d31cb663964da47e83e2 to your computer and use it in GitHub Desktop.
Save SupaFuzz/d9e51c512d75d31cb663964da47e83e2 to your computer and use it in GitHub Desktop.
Promises and Promise.all() and catch() before then()

Promises Don't Work Like You Might Think

The order of .catch() and .then() is crucial

new Promise(function(resolve, reject){
  setTimeout(function(){ reject('an error happened!'); }, 1500);
}).catch(function(error){
  console.log(`error: ${error}`);
}).then(function(result){
  console.log("all done!");
});

"all done" will fire every time even if the promise is rejected.

new Promise(function(resolve, reject){
  setTimeout(function(){ reject('an error happened!'); }, 1500);
}).then(function(result){
  console.log("all done!");
}).catch(function(error){
  console.log(`error: ${error}`);
});

now "all done" only fires if catch() doesn't. makes no sense, and having the catch immediately adjascent to the code you tried to execute is hella more readable to me, but there you have it.

If, like me, you're hellbent on having your catches up front you can catch it with a variable external to the catch/then scope but it's still kinda nasty:

let abort = false;
new Promise(function(resolve, reject){
  setTimeout(function(){ reject('an error happened!'); }, 1500);
}).catch(function(error){
  abort = true;
  console.log(`error: ${error}`);
}).then(function(result){
  if (! abort){ console.log("all done!"); }
});

Promise.all() with catch first

if you wanna do the catch first with a Promise.all() you need to be aware that the .catch() function will fire immediately on the first rejected promise in the array, regardless of the resolution status of the remaining promises in the array

let blah = [];
blah.push(new Promise(function(t,b){  setTimeout(function(){
  console.log('resolve 1500');
  t(true); 
}, 1500); }));
blah.push(new Promise(function(t,b){  setTimeout(function(){ 
  console.log('resolve 2500');
  t(true); 
}, 2500); }));
blah.push(new Promise(function(t,b){  setTimeout(function(){
  console.log('reject 150');
  b('wtf'); 
}, 150); }));

let abrt = false;
Promise.all(blah).catch(function(error){
  console.log(`error: ${error}`);
  abrt = true;
}).then(function(){
  if (! abrt){ console.log("all done"); }
});

the above will fail to fire the "all done", true enough, but it fires the "error: " console.log call before the other promises in the array resolve.

Your catch() block will fire before all promises in the Promise.all() resolve.

putting the catch() block last does not change the behavior beyond no longer needing the abort flag to prevent the then() block running:

let blah = [];
  blah.push(new Promise(function(t,b){  setTimeout(function(){
    console.log('resolve 1500');
    t(true); 
  }, 1500); }));
  blah.push(new Promise(function(t,b){  setTimeout(function(){ 
    console.log('resolve 2500');
    t(true); 
  }, 2500); }));
  blah.push(new Promise(function(t,b){  setTimeout(function(){
    console.log('reject 150');
    b('wtf'); 
  }, 150); }));
  
  Promise.all(blah).then(function(){
    console.log("all done");
  }).catch(function(error){
    console.log(`error: ${error}`);
  });

Which I suppose makes sense as Promise.all() itself returns a promise that behaves like every other promise. That being the case, I don't see this behavior called out in either the WC3 spec nor the vernerable MDN docs. Seemed worth mentioning somewhere on the internet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment