简体   繁体   中英

How can I use promises to catch errors when they might not be wrapped in a promise?

Background

I am using Promises, and I have a multitude of functions that may or may not return a Promise and that may or may not fail, like in the example below:

//does not return a Promise, simply a string
let goodFun = function(){
    return "I like bananas!";
};

//blows up!
let badFun = function(){
    throw "A general error blaahh!"; 
};

//You get the point ...

Since these functions may or may not return Promises and may or may not fail, I need to wait for all of them to execute. To achieve this I have a function to call them all and wait for their execution:

let asyncFun = function(){
    return Promise.all([badFun(), goodFun()]);
};

Problem

So far so good. My code calls asyncFun and I expect some of its functions to actually fail. To be prepared, I added a catch:

let executor = function(){

    let numbsArray = [1, 2, 3];
    let respArray = [];

    for(let num of numbsArray){
        respArray.push(
            asyncFun()
                .catch( error => console.log(`I failed with ${error} and ${num}`))
        );
    }

    return Promise.all(respArray);
};

The problem is that catch is not catching anything at all!

Even adding a catch to the function calling the executor is not catching anything !

executor()
    .catch(error => console.log("Failed miserably to catch error!"));

Research

I don't really understand why my catch clauses are not catching the exception. To find out, I read this discussion:

Which leads me to believe that all my functions goodFun and badFun , no matter what, must always return a promise.

This is confusing for me, because according to the MDN documentation the array may contain a Promise, or a result from one (like a string, or a number).

I would also like to avoid adding even more boiler plate code to my functions ....

Question:

  1. How do I fix my code, so the catches work adding a minimum or boilerplate code?

Code

 let goodFun = function() { return "I like bananas!"; }; let badFun = function() { throw "A general error blaahh!"; }; let asyncFun = function() { return Promise.all([badFun(), goodFun()]); }; let executor = function() { let numbsArray = [1, 2, 3]; let respArray = []; for (let num of numbsArray) { respArray.push( asyncFun() .catch(error => console.log(`I failed with ${error} and ${num}`)) ); } return Promise.all(respArray); }; executor() .catch(error => console.log("Failed miserably to catch error!"));

The reason your .catch es are not working is that the errors are being thrown synchronously . The code execution never even executes that .catch() instruction to set up a catch handler because the error has already been thrown and the code execution has gone elsewhere. If you wrapped everything in an ordinary try-catch , I think you'd see it catching the errors you're throwing.

Example:

 let goodFun = function() { return "I like bananas!"; }; //blows up! let badFun = function() { throw "A general error blaahh!"; }; try { Promise.all([goodFun(), badFun()]) .then(results => console.log(results)) .catch(error => console.error(error)) } catch (e) { console.error("Wups, I caught an error. This wasn't supposed to happen.", e); }

This is confusing for me, because according to the MDN documentation the array may contain a Promise, or a result from one (like a string, or a number).

The MDN documentation is absolutely correct, but if badFun() throws an error, there's nothing that Promise.all can do about it, because the the program flow will be going down the call stack looking for something to catch the error that was thrown.

If you're not sure whether some code is synchronous or promise-based (or if it will throw a synchronous error), you can use this to safely wrap it a promise:

function executeSafe(action) {
    return Promise.resolve().then(action);
}

This will shield action() from any "boom" that takes place.

Example:

 let executeSafe = action => Promise.resolve().then(action); let goodFun = () => "I like bananas!"; //blows up! let badFun = () => { throw "A general error blaahh!" }; Promise.all([goodFun, badFun].map(executeSafe)) .then(results => console.log(results)) .catch(error => console.error(error))

FYI, if you're using Bluebird, then it has a built-in method Promise.try , that serves the same purpose as executeSafe above: Promise.all([goodFun, badFun].map(Promise.try)) .

Here's your full example with the needed modifications:

 let executeSafe = action => Promise.resolve().then(action); let goodFun = () => "I like bananas!"; //blows up! let badFun = () => { throw "A general error blaahh!" }; let asyncFun = () => Promise.all([goodFun, badFun].map(executeSafe)); let executor = function() { let numbsArray = [1, 2, 3]; return Promise.all(numbsArray.map(num => asyncFun() .catch(error => console.log(`I failed with ${error} and ${num}`)) )); } executor();

And here it is with the .catch outside of the executor :

 let executeSafe = action => Promise.resolve().then(action); let goodFun = () => "I like bananas!"; //blows up! let badFun = () => { throw "A general error blaahh!" }; let asyncFun = () => Promise.all([goodFun, badFun].map(executeSafe)); let executor = function() { let numbsArray = [1, 2, 3]; return Promise.all(numbsArray.map(asyncFun)); } executor().catch(error => console.error("Miserably failed to catch anything.", error));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM