Promise.all for Rejections and Resolves

By  on  

Promises have been an incredible addition to JavaScript; they save us callback hell, make coding async more maintainable, and and allow us to keep track of multiple async processes at a time.  Promise.all comes to mind, allowing us to react when multiple promises have been resolved.  Unfortunately Promise.all only resolves when all promises are resolved, so if any of the promises fail, the catch is called instead of then:

Promise.all([
    Promise.resolve(1),
    Promise.reject(0)
])
.then(() => { console.log('resolved!'); })
.catch(() => { console.log('failed!') });
// >> failed!

This is a problem if you want the same functionality to be executed regardless of if any promise in the array is rejected.  You could provide the same function to then and catch but that could lead to maintenance issues and occasional "WTF IS THIS?!" comments from other engineers.

So what should we do when we want Promise.all to trigger functionality regardless of any rejections?  Jake Archibald has the answer:

Promise.all(promises.map(p => p.catch(() => undefined)));

Each promise's catch callback returns undefined which allows the promise's failure to be handled as success. To prove it works, consider this snippet:

Promise.all([
    // Resolves
    Promise.resolve(1), 
    // Rejects after 2 seconds
    new Promise((resolve, reject) => setTimeout(() => reject(1), 2000))
].map(p => p.catch(() => undefined))).then(() => console.log('done!'));

// >> done!

Despite the second promise being rejected, the Promise.all then is called! In the future we'll be able to use Promise.prototype.finally to more easily handle success and failure.

Thank you to Jake for this awesome trick!

Recent Features

  • By
    5 More HTML5 APIs You Didn’t Know Existed

    The HTML5 revolution has provided us some awesome JavaScript and HTML APIs.  Some are APIs we knew we've needed for years, others are cutting edge mobile and desktop helpers.  Regardless of API strength or purpose, anything to help us better do our job is a...

  • By
    Responsive Images: The Ultimate Guide

    Chances are that any Web designers using our Ghostlab browser testing app, which allows seamless testing across all devices simultaneously, will have worked with responsive design in some shape or form. And as today's websites and devices become ever more varied, a plethora of responsive images...

Incredible Demos

  • By
    MooTools ASCII Art

    I didn't realize that I truly was a nerd until I could admit to myself that ASCII art was better than the pieces Picasso, Monet, or Van Gogh could create.  ASCII art is unmatched in its beauty, simplicity, and ... OK, well, I'm being ridiculous;  ASCII...

  • By
    CSS Tooltips

    We all know that you can make shapes with CSS and a single HTML element, as I've covered in my CSS Triangles and CSS Circles posts.  Triangles and circles are fairly simply though, so as CSS advances, we need to stretch the boundaries...

Discussion

  1. Fred

    Have you tried RQ, from our famous and beloved Douglas Crockford, a great (and tiny) async library?

    http://www.rq.crockford.com/

  2. J

    This was on StackOverflow about a year ago http://stackoverflow.com/a/36115549/1253312

  3. Matt

    Howdy David,

    Nice find! I was really struggling with this until I found this method of handling errors.

    Rather than just leaving the rejected promise as undefined, if you add in a flag in the reject event, you can catch the error message cleanly and process in the then function, eg:

    Promise.all( arrayOfPromises
    					.map(p => p.catch( (err) => err)) )
    		.then(data => {
    			console.log(data); // you also get err thrown in too, yay!
    		});

    Matt

  4. Alexon da Silva Moreira

    Hello, I have these two query using promise.using, with bluebird, I can only execute one or the other, how do I create a promise all to execute the two function promise and also other function.

    I have:

    **** Promise 1 ****
    
    function fntTotalDeRegistros () {
    promise.using (db, () => {
    db.query ('SELECT COUNT (*) FROM CUSTOMER')
    .then ((rows) => {
    rows.forEach ((row) => {
    qtdRegsARQ = row.registers});
    console.log (qtdRegsARQ)
    })
    .then () => db.close ())
    .catch ((error) => {console.log (error), db.close ()});
    })
    };
    
    **** Promise 2 ****
    
    function fntRegisterCli () {
    promise.using (db, () => {
    db.query ('Select * from DB_ICADIS.CLIENTE ORDER BY cli_codigo')
    .then ((rows) => {
    rows.forEach ((row) => {
    ...........
    });
    })
    .then (() => console.log (regsCLI))
    .then () => db.close ())
    .catch ((error) => {console.log (error), db.close ()});
    })
    };
    
    
    **** function 3 *******
    
    function fntRegistrationFtr () {
    regFTR = func.concatLeft ('FTR', 3, '0');
    qtdRegsARQ = func.concatLeft (qtdRegsARQ, 9, '0');
    resFTR = func.concatRight ('RESERVED', 202, '');
    seqARQ = func.concatRight (seqARQ, 6, '0');
    regsFTR = $ {regFTR} $ {qtdRegsARQ} $ {resFTR} $ {seqARQ} \ n;
    console.log (regsFTR)
    return (regsFTR);
    }
    
  5. Jorge

    The method explained in the article would masquerade errors and unhandled exceptions.
    This post should be updated with the solution in here:
    https://stackoverflow.com/a/32979111/3221029

    Plus Promise.finally is already supported.

  6. Daniel Cohen Gindi

    Using Promise.finally will not help you there, as it does not suppress errors.
    Meaning – the Promise will still throw after you’ve chained it with a finally call.
    Using Promise.catch is still your best bet!

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!