【问题标题】:How do I catch ES6 Promise rejections and completely stop flow?如何捕获 ES6 Promise 拒绝并完全停止流程?
【发布时间】:2016-03-30 15:28:42
【问题描述】:

假设我有 4 个函数:runA()runB()runC()runD()

使用 ES6 承诺,在完全成功的运行中,这些都将一个接一个地运行:

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

如果runArunB 失败(拒绝或抛出),我想调用error1() 然后完全停止链(而不是调用runCrunD)。这让我觉得我应该在.then 承诺链的最后添加一个.catch()

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)

但如果runC 失败,我想调用error2() 并仍然停止链(而不是调用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)

现在我在链中有 2 个 catch 调用,runC 将在 error1 运行后被调用,因为捕获的结果将默认为 resolve。让error1 函数创建一个它总是拒绝的承诺是我唯一的选择吗?

【问题讨论】:

  • 只使用一个.catch()有什么问题?您可以在 catch 回调 (if (error1) error1() else if (error2) error2()...) 中进行错误分类。您抛出的 Error 对象可以包含消息和名称(可以是您需要的类型,例如“RunCError”)。
  • 当然,您的意思是runA() .then(runB) .then(runC) .then(runD) - ...除此之外,如果您真的无法确定导致错误的原因,您可以在最后throw error1 跳过 runCrunD - 但您需要在 error2 中确定错误来自 A 或 B 而不是 C 或 D

标签: javascript es6-promise


【解决方案1】:

不,让error1 创建一个总是拒绝的承诺,不是你唯一的选择。

您可以利用.then 接受两个参数这一事实:

.then(onSuccess, onFailure)

当给定两个参数时,onFailure不会捕获onSuccess 中的故障,这一效果被低估了。这通常是不可取的,除了在这里,您可以使用这个事实来分支您的决策树:

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

这就是你想要的。

  • 如果 runArunB 失败,则调用 error1 并停止链。
  • 如果 runCrunD 失败,则调用 error2 并停止链。

你也可以这样写:

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)
&lt;div id="div"&gt;&lt;/div&gt;

尝试修改this fiddle中哪个失败。

【讨论】:

  • 绝对是一种有用的技术,也是我一开始尝试使用的技术。我的问题是,如果我想在链中进一步捕获异常或拒绝,来自第二个 then 参数的错误回调将导致外链解决......除非错误回调本身引发另一个异常或被拒绝,在这种情况下,它会导致下一个 catch 语句被命中,这两者都不是我真正想要发生的。我在这里有updated your fiddle 的示例问题。
  • 好吧,除非您明显遵循我的回答,否则它不会起作用。你必须像我在小提琴中展示的那样分支链条,而你没有这样做。
  • 再看看。我确实对其进行了分支,但在分支之后还有另一个 then 语句。所以你的答案只适用于分支是链的最后一部分。
  • 我的解决方案有你问的四个功能。如果要拒绝一个外链,应该拒绝什么错误?如果你只是想在没有成功或失败的情况下停止链条,你可以做.then(new Promise()),但这不是一个好主意,即使我试图不去判断。
【解决方案2】:

只使用一个.catch() 有什么问题?您可以在 catch 回调 (if (error1) error1() else if (error2) error2()...) 中进行错误分类。您抛出的 Error 对象可以有一条消息和一个名称(可以是您需要的类型,例如“RunCError”)。

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...
}

【讨论】:

  • 那么你很少会想要在一个承诺链中使用超过 1 个 catch 吗?如果你确实包含超过 1 个 catch,并且你不想要下一个 then要在 catch 之后被触发,catch 中的函数需要再次抛出或拒绝。
  • 是的。它非常类似于同步代码。如果你捕捉到一个异常,那么它就会被捕捉、处理并恢复正向的代码流。这就是 catch 的用途。如果那不是您想要的,那么要么一开始就不要抓住它,要么重新扔掉它。在这种情况下,如果您嵌套捕获,那么您希望外部的捕获也能捕获它,对吧?
【解决方案3】:

我刚刚偶然发现了同样的问题,到目前为止,我的解决方案是在 catch 中显式调用 reject,就像在这个 js bin 中一样:https://jsbin.com/yaqicikaza/edit?js,console

代码sn-p

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

【讨论】:

    猜你喜欢
    • 2021-04-21
    • 2016-05-18
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    相关资源
    最近更新 更多