javascript - 如何从 Promise 的 catch/then block 中返回?

标签 javascript asynchronous promise

有很多教程介绍如何在使用 JavaScript Promise 编程时使用“then”和“catch”。然而,所有这些教程似乎都忽略了一个重点:从 then/catch block 返回以打破 Promise 链。让我们从一些同步代码开始来说明这个问题:

try {
  someFunction();
} catch (err) {
  if (!(err instanceof MyCustomError))
    return -1;
}
someOtherFunction();

本质上,我正在测试一个捕获的错误,如果不是我期望的错误,我将返回给调用者,否则程序将继续。但是,此逻辑不适用于 Promise:

Promise.resolve(someFunction).then(function() {
  console.log('someFunction should throw error');
  return -2;
}).catch(function(err) {
   if (err instanceof MyCustomError) {
     return -1;
   }
}).then(someOtherFunction);

此逻辑用于我希望函数以某种方式失败的一些单元测试。即使我将 catch 更改为 then block ,我仍然无法破坏一系列链式 Promise,因为从 then/catch block 返回的任何内容都将成为沿着链传播的 Promise。

不知道Promise能不能实现这个逻辑;如果不是,为什么? Promise 链永远不会被破坏,这对我来说很奇怪。谢谢!

2015 年 8 月 16 日编辑: 根据目前给出的答案,then block 返回的被拒绝的 Promise 将通过 Promise 链传播并跳过所有后续的 then block ,直到被捕获(处理)。这种行为很好理解,因为它只是模仿以下同步代码(方法 1):

try {
  Function1();
  Function2();
  Function3();
  Function4();
} catch (err) {
  // Assuming this err is thrown in Function1; Function2, Function3 and Function4 will not be executed
  console.log(err);
}

但是,我要问的是同步代码中的以下场景(方法 2):

try {
  Function1();
} catch(err) {
  console.log(err); // Function1's error
  return -1; // return immediately
}
try {
  Function2();
} catch(err) {
  console.log(err);
}
try {
  Function3();
} catch(err) {
  console.log(err);
}
try {
  Function4();
} catch(err) {
  console.log(err);
} 

我想以不同的方式处理不同函数中引发的错误。如方法 1 中所示,我有可能在一个 catch block 中捕获所有错误。但那样我必须在 catch block 内创建一个大的 switch 语句来区分不同的错误;而且,如果不同函数抛出的错误没有共同的可切换属性,我将根本无法使用 switch 语句;在这种情况下,我必须为每个函数调用使用单独的 try/catch block 。方法 2 有时是唯一的选择。 Promise 的 then/catch 语句不支持这种方法吗?

最佳答案

这无法通过语言的特性来实现。但是,可以使用基于模式的解决方案。

这里有两种解决方案。

重新抛出之前的错误

这个模式基本上是健全的......

Promise.resolve()
.then(Function1).catch(errorHandler1)
.then(Function2).catch(errorHandler2)
.then(Function3).catch(errorHandler3)
.then(Function4).catch(errorHandler4)
.catch(finalErrorHandler);

Promise.resolve() 不是绝对必要的,但允许所有 .then().catch() 行具有相同的模式,并且整个表情看起来更舒服。

...但是:

  • 如果 errorHandler 返回结果,则链将前进到下一行的成功处理程序。
  • 如果 errorHandler 抛出,则链将前进到下一行的错误处理程序。

除非错误处理程序被编写成能够区分先前抛出的错误和新抛出的错误,否则所需的链跳出不会发生。例如:

function errorHandler1(error) {
    if (error instanceof MyCustomError) { // <<<<<<< test for previously thrown error 
        throw error;
    } else {
        // do errorHandler1 stuff then
        // return a result or 
        // throw new MyCustomError() or 
        // throw new Error(), new RangeError() etc. or some other type of custom error.
    }
}

现在:

  • 如果 errorHandler 返回结果,则链将前进到下一个 FunctionN。
  • 如果 errorHandler 抛出 MyCustomError,那么它将被重复地沿着链向下抛出并被第一个不符合 if(error instanceof MyCustomError) 协议(protocol)的错误处理程序捕获(例如 final .catch()).
  • 如果 errorHandler 抛出任何其他类型的错误,则链将进行到下一个捕获。

如果您需要根据抛出的错误类型灵活地跳到链尾或不跳到链尾,此模式将很有用。我预计会出现罕见情况。

DEMO

隔热扣

另一种解决方案是引入一种机制来保持每个 .catch(errorHandlerN)“隔离”,这样它只会捕获由对应的 FunctionN 引起的错误,而不是来自任何先前的错误。

这可以通过在主链中只有成功处理程序来实现,每个处理程序都包含一个包含子链的匿名函数。

Promise.resolve()
.then(function() { return Function1().catch(errorHandler1); })
.then(function() { return Function2().catch(errorHandler2); })
.then(function() { return Function3().catch(errorHandler3); })
.then(function() { return Function4().catch(errorHandler4); })
.catch(finalErrorHandler);

这里 Promise.resolve() 扮演着重要的 Angular 色。没有它,Function1().catch(errorHandler1) 将位于主链中,catch() 将不会与主链隔离。

现在,

  • 如果 errorHandler 返回结果,则链将前进到下一行。
  • 如果 errorHandler 抛出它喜欢的任何东西,那么链将直接前进到 finalErrorHandler。

如果您希望始终跳到链的末尾而不管抛出的错误类型如何,请使用此模式。不需要自定义错误构造函数,也不需要以特殊方式编写错误处理程序。

DEMO

使用案例

选择哪种模式将取决于已经给出的考虑因素,但也可能取决于项目团队的性质。

  • 单人团队 - 你写下所有内容并理解问题 - 如果你可以自由选择,然后按照你的个人喜好运行。
  • 多人团队 - 一个人编写主链,其他人编写函数及其错误处理程序 - 如果可以,选择绝缘捕获 - 一切都在主链的控制下,你不需要强制执行以特定方式编写错误处理程序的纪律。

关于javascript - 如何从 Promise 的 catch/then block 中返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32032588/

相关文章:

javascript - 加载失败 - 跨源请求是 - Angularjs 1.x - ngRoute

javascript - 让 html 元素闪烁背景颜色并淡入另一种颜色

.net - 合并不同类型的 .NET 4.0 任务/延续

javascript - NodeJS - 使用返回的 Promise 更新对象

javascript - 在 RxJS 中链接 Observable

javascript - 如何在 ES6 中重试 Promise

javascript - 是否可以将输入数组传播到参数中?

javascript - 如何使框的底部在 FullCalendar 中的新元素的 'append' 上移动

javascript - 使用单个 json 字符串填充多个选择框

c# - 在库中编写同步和异步方法并保持 DRY 的模式