How do I catch ES6 Promise rejections and completely stop flow?

一世执手 提交于 2020-01-01 12:07:51

问题


Say I have 4 functions: runA(), runB(), runC() and runD().

Using ES6 promises, in a completely successful run, these would all be run one after another:

runA()
.then(runB)
.then(runC)
.then(runD)

If runA or runB fail (reject or throw), I would like to call error1() and then completely stop the chain (not call runC or runD ). This makes me think I should add a single .catch() at the very end of the .then promise chain:

runA()
.then(runB)
.then(runC)     //won't get called if runA or runB throws
.then(runD)     //won't get called if runA or runB throws
.catch(error1)

But if runC fails, I would like to call error2() and still stop the chain (not call runD).

runA()
.then(runB)   
.catch(error1)  //moved up to only handle runA and runB
.then(runC)     //but now this gets called after error1() is run
.then(runD)     
.catch(error2)

Now that I have 2 catch calls in the chain, runC will get called after error1 is run since the result of the catch will default to a resolve. Is my only option to have the error1 function create a promise that it always rejects?


回答1:


No, having error1 create a promise that always rejects, is not your only option.

You can exploit the fact that .then takes two arguments:

.then(onSuccess, onFailure)

When given two arguments, there's an under-appreciated effect that onFailure will not catch failures in onSuccess. This is usually undesirable, except here, where you can use this fact to branch your decision tree:

runA()
.then(runB)
.then(() => runC().then(runD), error1)
.catch(error2)

This does what you want.

  • if runA or runB fail, then error1 is called and chain stops.
  • if runC or runD fail, then error2 is called and chain stops.

You can also write it like this:

runA()
.then(runB)
.then(() => runC()
  .then(runD)
  .catch(error2),
error1)

var log = msg => div.innerHTML += "<br>" + msg;

// Change which one of these four rejects, to see behavior:
var runA = () => Promise.resolve().then(() => log("a"));
var runB = () => Promise.reject().then(() => log("b"));
var runC = () => Promise.resolve().then(() => log("c"));
var runD = () => Promise.resolve().then(() => log("d"));
var error1 = () => log("error1");
var error2 = () => log("error2");

runA()
.then(runB)
.then(() => runC().then(runD), error1)
.catch(error2)
<div id="div"></div>

Try modifying which one fails in this fiddle.




回答2:


What's wrong with using only one .catch()? You can do error triage in the catch callback (if (error1) error1() else if (error2) error2()...). The Error object you throw can have a message and a name (could be the type like you need, 'RunCError' for example).

runA()
    .then(runB)
    .then(runC)     // won't get called if runA or runB throws
    .then(runD)     // won't get called if runA or runB throws
    .catch(handleErrors)

function runA() {
    // ...

    if (err) {
        var error = new Error('Something is wrong...');
        error.name = 'RunAError';
        throw error;
    }
}

function runB() {
    // ...

    if (err) {
        var error = new Error('Something is wrong...');
        error.name = 'RunBError';
        throw error;
    }
}

function runC() {
    // ...

    if (err) {
        var error = new Error('Something is wrong...');
        error.name = 'RunCError';
        throw error;
    }
}

function runD() {
    // ...

    if (err) {
        var error = new Error('Something is wrong...');
        error.name = 'RunDError';
        throw error;
    }
}

function handleErrors(err) {
    if (err.name == 'RunAError') {
        handleAError();
    }

    if (err.name == 'RunBError') {
        handleBError();
    }

    // so on...
}



回答3:


I just stumbled on the same problem and my solution is so far to explicitly invoke a reject in a catch like in this js bin: https://jsbin.com/yaqicikaza/edit?js,console

Code snippet

const promise1 = new Promise( ( resolve, reject ) => reject( 42 ) );

promise1
  .catch( ( err ) => console.log( err ) ) // 42 will be thrown here
  .then( ( res ) => console.log( 'will execute' ) ) // then branch will execute


const promise2 = new Promise( ( resolve, reject ) => reject( 42 ) );

promise2
  .catch( ( err ) => Promise.reject( ) ) // trigger rejection down the line
  .then( ( res ) => console.log( 'will not execute' ) ) // this will be skipped


来源:https://stackoverflow.com/questions/34448493/how-do-i-catch-es6-promise-rejections-and-completely-stop-flow

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!