【问题标题】:Chaining Javascript promises链接 Javascript 承诺
【发布时间】:2016-07-19 06:38:57
【问题描述】:

我正在尝试从 MDN documentation 中理解 Promise。第一个示例演示了thencatch 方法:

// We define what to do when the promise is resolved/fulfilled with the then() call,
// and the catch() method defines what to do if the promise is rejected.
p1.then(
    // Log the fulfillment value
    function(val) {
        log.insertAdjacentHTML('beforeend', val +
            ') Promise fulfilled (<small>Async code terminated</small>)<br/>');
    })
.catch(
    // Log the rejection reason
    function(reason) {
        console.log('Handle rejected promise ('+reason+') here.');
    });

文档指出then 方法返回一个新的承诺,所以上面的代码不应该等同于

var p2 = p1.then(
    // Log the fulfillment value
    function(val) {
        log.insertAdjacentHTML('beforeend', val +
            ') Promise fulfilled (<small>Async code terminated</small>)<br/>');
    });
p2.catch(
    // Log the rejection reason
    function(reason) {
        console.log('Handle rejected promise ('+reason+') here.');
    });

?

如果是这样,那是否意味着只有当从p1.then 返回的承诺而不是承诺p1 被拒绝时才会调用catch 回调?我不需要这样做吗:

p1.then( /* etc. */ );
// and for rejected resolutions
p1.catch( /* etc. */ );

捕捉对承诺 p1 的拒绝,而不是将 catch 链接到 then

起初,我认为从p1.then 返回的承诺与p1 相同,就像 jQuery 对其大部分 API 所做的那样。但是下面清楚地表明这两个promise是不同的。

var p1 = new Promise(function(resolve, reject) { 
  resolve("Success!");
});

console.log(p1);
// Promise { <state>: "fulfilled", <value>: "Success!" }

var p2 = p1.then(function(value) {
  console.log(value);
});
// Success!

console.log(p2); 
// Promise { <state>: "fulfilled", <value>: undefined }

另外,我在 JSFiddle 中使用了三种方法:

  1. p1.then(onFulfilled).catch(onRejected);
  2. p1.then(onFulfilled); p1.catch(onRejected);
  3. p1.then(onFulfilled, onRejected);

这三个都有效。我可以理解后两者。我的问题的要点是,为什么第一种方法也有效?

【问题讨论】:

  • 一个可能的错误会通过 Promise 链传播,直到找到合适的处理程序。因此,即使 p2 承诺与 p1 不同,p1 中的任何错误(拒绝)都会传播到 p2,除非 p1 有单独的catch()
  • .catch(onRejected) 视为.then(null, onRejected) (因为在早期的promise 实现中,基本上就是这样——你理解了后两个,你应该很好地理解第一个
  • @JaromandaX:仍然是,不确定您所说的“早期承诺实施”是什么意思。
  • @bergi ... .catch 在我阅读的第一个 Promises 规范中甚至没有提到,以至于我早期使用 Promise 经常使用 .then(null, onError) 类型代码(我的记忆虽然可能会感到困惑)
  • @JaromandaX:当然,但从它被引入的角度来看(可能是here),它是then(null, onRejected)的别名

标签: javascript promise chaining


【解决方案1】:

首先,介绍一下 Promise 相关部分的工作原理:

p1.then(...) 确实返回了一个链接到前一个承诺的新承诺。因此,p1.then(...).then(...) 将仅在第一个处理程序完成后执行第二个.then() 处理程序。而且,如果第一个 .then() 处理程序返回一个未完成的承诺,那么它将等待返回的承诺解决,然后再解决第二个承诺并调用第二个 .then() 处理程序。

其次,当 Promise 链拒绝链中的任何位置时,它会立即向下跳过链(跳过任何已完成的处理程序)直到它到达第一个拒绝处理程序(无论它来自第二个参数的 .catch() .then())。这是 Promise 拒绝的一个非常重要的部分,因为这意味着您不必在 Promise 链的每个级别都捕获拒绝。您可以在链的末尾放置一个.catch(),并且在链中任何地方发生的任何拒绝都将直接转到该.catch()

另外,值得了解的是.catch(fn) 只是.then(null, fn) 的快捷方式。它的工作原理没有什么不同。

另外,请记住(就像.then().catch() 也将返回一个新的承诺。如果您的 .catch() 处理程序本身没有抛出或返回被拒绝的承诺,那么拒绝将被视为“已处理”并且返回的承诺将解决,从而允许链从那里继续。这使您可以处理错误,然后有意识地决定是否希望链继续正常执行逻辑或保持被拒绝。

现在,针对您的具体问题...

如果是这样,那是否意味着将调用 catch 回调 仅当承诺从 p1.then 返回时,而不是承诺 p1, 决定拒绝?我不需要这样做吗:

没有。 拒绝立即沿链向下传播到下一个拒绝处理程序,跳过所有解析处理程序。因此,在您的示例中,它将沿链向下传递到下一个 .catch()

这是使用 Promise 使错误处理变得如此简单的原因之一。您可以将.catch() 放在链的末尾,它会从链中的任何位置捕获错误。

有时有理由在链的中间拦截错误(如果您想分支并更改错误的逻辑,然后继续使用其他代码)或者如果您想“处理”错误并继续进行.但是,如果您的链是全有或全无,那么您只需在链的末尾放置一个.catch() 即可捕获所有错误。

它类似于同步代码中的 try/catch 块。将.catch() 放在链的末尾就像将一个try/catch 块放在一堆同步代码周围的最高级别。它会在代码中的任何地方捕获异常。

这三个都有效。我可以理解后两者。我的要点 问题是,为什么第一种方法也有效?

这三个都差不多。 2 和 3 是相同的。其实.catch(fn) 只不过是.then(null, fn) 的快捷方式。

选项 1 略有不同,因为如果 onFulfilled 处理程序抛出或返回一个被拒绝的承诺,那么 .catch() 处理程序将被调用。在其他两个选项中,情况并非如此。除了这一点不同之外,它的工作原理是一样的(正如您所观察到的)。

选项 1 有效,因为拒绝沿链传播。因此,如果 p1 拒绝或 onFulfilled 处理程序返回拒绝的承诺或抛出,则将调用 .catch() 处理程序。 p>

【讨论】:

    【解决方案2】:

    代码不应该是等价的

    他们是。

    如果是这样,那岂不是意味着只有当从p1.then 返回的promise(而不是promise p1)被解析为rejected 时才会调用catch 回调?

    是的,没错。

    但是,当 p1 拒绝时,p2 也会这样做,因为您没有将 onRejected 处理程序传递给会拦截它的 .then() 调用。拒绝只是沿着链条传播。

    我在 JSFiddle 中使用了这三种方法。这三个都有效。

    他们这样做,但他们不这样做。

    p1.then(onFulfilled, onRejected);
    

    这是你通常想要做的。

    p1.then(onFulfilled); p1.catch(onRejected);
    

    这会产生两个不同的承诺,一个将通过onFulfilled 结果解决或拒绝,另一个将通过onRejected 结果实现或解决。

    p1.then(onFulfilled).catch(onRejected);
    

    这是与第一个不同的野兽,请参阅When is .then(success, fail) considered an antipattern for promises?

    【讨论】:

      【解决方案3】:

      这个:

      var p2 = p1.then()
      p2.catch()
      

      和这个是一样的:

      p1.then().catch()
      

      你也可以这样做:

      p1
       .then(response => response.body)
       .then(body => JSON.parse(body))
       .then(data => console.log(data))
       .catch(e => console.log('something somewhere failed'))
      

      【讨论】:

        猜你喜欢
        • 2023-01-27
        • 2016-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-13
        • 2017-02-20
        • 2018-07-26
        相关资源
        最近更新 更多