链接 Javascript 个承诺

Chaining Javascript promises

我正在尝试从 MDN documentation 中理解 Promises。第一个示例演示了 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 方法 returns 是一个新的承诺,所以上面的代码不应该等同于

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 返回时才会调用 catch 回调,而不是承诺 p1 被拒绝?我不是必须这样做吗:

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 的相关部分如何工作的背景知识:

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

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

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

此外,请记住(就像 .then().catch() 也将 return 一个新的承诺。 Any 如果你的 .catch() 处理程序本身没有抛出或 return 一个被拒绝的承诺,那么拒绝将被视为 "handled" 并且 returned 承诺将解决,允许链从那里继续。这允许您处理错误,然后有意识地决定您是希望链继续正常执行逻辑还是保持被拒绝状态。

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

If so, then wouldn't that mean that the catch callback would be called only if the promise returned from p1.then, not the promise p1, resolved to rejected? And wouldn't I have to do this:

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

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

有时有理由在链的中间拦截错误(如果你想分支并更改错误的逻辑,然后继续处理其他代码)或者如果你想 "handle"错误并继续。但是,如果你的链是全有或全无,那么你可以只在链的末尾放一个 .catch() 来捕获所有错误。

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

All three work. I can understand the latter two. The gist of my question is, why does the first approach also work?

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

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

选项 1 有效,因为拒绝沿着链传播。因此,如果 p1 拒绝或者如果 onFulfilled 处理程序return是一个被拒绝的承诺或抛出,那么 .catch() 处理程序将被调用。

shouldn't the codes be equivalent

他们是。

If so, then wouldn't that mean that the catch callback would be called only if the promise returned from p1.then, not the promise p1, resolved to rejected?

是的,完全正确。

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

I played around in JSFiddle with the three approaches. All three work.

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

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?

这个:

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'))