当前位置: 首页 > 知识库问答 >
问题:

如何从promise的捕获/然后阻止中返回?

郎恺
2023-03-14

有许多关于如何在使用JavaScript Promise编程时使用“然后”和“捕获”的教程。然而,所有这些教程似乎都忽略了一个重要的点:从然后/捕获块返回以打破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块,我仍然无法打破一系列链式promise,因为从then/catch块返回的任何内容都将成为沿链传播的promise。

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

编辑于08/16/2015:根据到目前为止给出的答案,被拒绝的promise返回的然后块将通过promise链传播,并跳过所有后续的然后块,直到被捕获(处理)。这种行为很好理解,因为它简单地模拟了以下同步代码(方法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);
} 

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

共有3个答案

司徒浩思
2023-03-14

没有内置的功能可以根据您的请求跳过整个剩余链。但是,您可以通过在每个捕获中抛出特定错误来模拟此行为:

doSomething()
  .then(func1).catch(handleError)
  .then(func2).catch(handleError)
  .then(func3).catch(handleError);

function handleError(reason) {
  if (reason instanceof criticalError) {
    throw reason;
  }

  console.info(reason);
}

如果任何捕获块捕获了CrialError,它们将直接跳到末尾并抛出错误。任何其他错误都将被控制台记录下来,然后继续到下一个。然后块。

宗增
2023-03-14

首先,我在这段代码中看到了一个常见错误,可能会让您完全困惑。这是您的示例代码块:

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

您需要将函数引用传递给。然后()handler,而不是实际调用函数并传递其返回结果。因此,上述代码可能是:

Promise.resolve(someFunction()).then(function() {
  console.log('someFunction should throw error');
  return -2;
}).catch(function(err) {
   if (err instanceof MyCustomError) {
     // returning a normal value here will take care of the rejection
     // and continue subsequent processing
     return -1;
   }
}).then(someOtherFunction);    // just pass function reference here

请注意,我在中的函数之后删除了()。然后()handler,因此您只是传递函数引用,而不是立即调用函数。这将允许promise基础设施决定是否在将来调用promise。如果你犯了这个错误,它将完全抛弃你的promise是如何运作的,因为事情将被称为无论如何。

关于捕捉拒绝的三个简单规则。

  1. 如果没有人发现拒绝,它会立即停止promise链,最初的拒绝将成为promise的最终状态。不调用后续处理程序
  2. 如果捕获到promise拒绝,并且拒绝处理程序未返回任何内容或返回任何正常值,则拒绝被视为已处理,promise链将继续并调用后续处理程序。从拒绝处理程序返回的任何内容都将成为promise的当前值,就像拒绝从未发生一样(除了未调用此级别的解析处理程序-而是调用了拒绝处理程序)
  3. 如果捕获到promise拒绝,并且您从拒绝处理程序中抛出错误或返回拒绝的promise,则将跳过所有解析处理程序,直到链中的下一个拒绝处理程序。如果没有拒绝处理程序,那么promise链将停止,新产生的错误将成为promise的最终状态

您可以在这个JSFIDLE中看到几个示例,其中显示了三种情况:

>

  • 从拒绝处理程序返回常规值会导致下一个。然后()解析要调用的处理程序(例如,继续正常处理),

    抛出拒绝处理程序会导致正常的解析处理停止,并跳过所有解析处理程序,直到到达拒绝处理程序或链的末尾。如果在解析处理程序中发现意外错误,这是停止链的有效方法(我认为这是您的问题)。

    不存在拒绝处理程序会导致正常的解析处理停止,并跳过所有解析处理程序,直到到达拒绝处理程序或链的末尾。

  • 易昌翰
    2023-03-14

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

    这里有两个解决方案。

    重新抛出之前的错误

    这种模式基本上是合理的。。。

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

    Promise.resolve()不是严格必需的,但允许所有. so(). cat()行具有相同的模式,并且整个表达式更容易理解。

    ... 但是:

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

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

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

    现在:

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

    如果您需要灵活地跳到链的末尾或不跳到链的末尾,此模式将非常有用,具体取决于抛出的错误类型。我认为这是罕见的情况。

    演示

    绝缘捕捉器

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

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

    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()起着重要作用。没有它,Function1()。catch(errorHandler1)将位于主链中catch()不会与主链隔离。

    现在

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

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

    演示

    用例

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

    • 一个人的团队——你写所有的东西,理解问题——如果你可以自由选择,那么按照你的个人偏好运行。
    • 多人团队-一个人编写主链,其他各种人编写函数及其错误处理程序-如果可以的话,选择绝缘捕获-所有东西都在主链的控制之下,您不需要强制执行以这种方式编写错误处理程序。
     类似资料:
    • 在 中,尝试捕获最终阻止的工作原理是什么? 所以如果有例外,我知道它会跳到捕获块,然后跳到最后的块。 但是如果没有错误,catch块不会运行,但是finally块会运行吗?

    • 我试图拦截来自所有promise然后方法的响应。但是我不能在原型然后方法中得到响应数据。请找到下面的代码。 在上面的代码中,我可以看到控制台打印在所有Promise调用中。但是我无法在then中获得响应对象。 控制台中打印的this对象值: “then”原型方法中的打印参数: 请建议我在then方法中获取所有promise方法的响应对象。 我尝试使用“arguments[0].arguments”

    • 我使用的是交互式代理的ibapi,我通常被困在如何捕获返回的数据上。例如,根据api文档,当我请求reqAccountSummary()时,该方法通过accountSummary()传递数据。但他们的例子只打印数据。我曾尝试捕获数据或将其分配给变量,但他们的文档中没有任何地方显示如何执行此操作。我也在谷歌搜索,只找到register()和registerAll(),但这是来自ib.opt的,它不在

    • 问题内容: 我需要兑现承诺,以便可以通过封闭函数将其返回。我知道,由于正常的JavaScript范围,我可能无法按照编码的方式进行操作。有什么办法可以做到吗? 位于#1 的 console.log 会生成正确的数据。 console.log #2总是产生’a’; 任何指针将不胜感激。 问题答案: 许诺背后的基本原则之一是它是异步处理的。这意味着您无法创建承诺,然后立即在代码中同步使用其结果(例如,

    • 我正在努力理解如何在Javascript中获得一个承诺的值,以便能够检查它是真的还是假的。 如果我console.log有效变量,它将返回以下内容: 在我的if语句中,我试图检查允诺值是否为真,但我不知道如何访问该值:/谁能告诉我如何检查该值吗? 谢谢

    • 原文是这样的: 这确实令人惊讶,但最终明白了(嗯,至少我想我明白了)为什么会这样发生。 现在,我将第二个更改为: 如果我遗漏了一些明显的东西,或者我的问题是愚蠢的,让我提前说声对不起。还有谢谢你!