链接Javascript承诺

时间:2016-07-19 06:38:58

标签: javascript promise chaining

我正在尝试从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方法返回一个新的promise,所以不要将上面的代码等同于

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.');
    });

如果是这样,那么这是否意味着仅当从catch而非承诺p1.then返回的承诺被拒绝时才会调用p1回调?我不是必须这样做的:

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

抓住拒绝承诺p1而不是将catch链接到then

起初,我认为从p1.then返回的承诺与p1相同,就像jQuery如何处理其大部分API一样。但以下清楚地表明这两个承诺是不同的。

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);
  4. 这三个都有效。我能理解后两者。我的问题的要点是,为什么第一种方法也有效?

3 个答案:

答案 0 :(得分:6)

首先,关于承诺的相关部分如何运作的一些背景知识:

p1.then(...)会返回一个链接到前一个的新承诺。因此,p1.then(...).then(...)仅在第一个处理程序完成后才会执行第二个.then()处理程序。并且,如果第一个.then()处理程序返回一个未实现的promise,那么它将在解析第二个promise并调用第二个.then()处理程序之前等待该返回的promise解析。

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

另外,值得理解的是.catch(fn)只是.then(null, fn)的捷径。它没有区别。

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

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

  

如果是这样,那么就不会意味着将调用catch回调   只有承诺从p1.then返回,而不是承诺p1,   决定拒绝?我不会这样做:

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

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

有时会有理由在链中间拦截错误(如果你想在错误上分支和更改逻辑,然后继续使用其他代码),或者如果你想要&#34;处理&#34;错误并继续前进。但是,如果您的链是全部或全部,那么您可以在链的末尾放置一个.catch()来捕获所有错误。

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

  

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

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

选项1略有不同,因为如果onFulfilled处理程序抛出或返回被拒绝的promise,则会调用.catch()处理程序。在另外两个选项中,情况并非如此。除了那个差异之外,它将起到相同的作用(如你所观察到的那样)。

选项1有效,因为拒绝在链中传播。因此,如果p1拒绝或者onFulfilled处理程序返回被拒绝的promise或throws,则将调用.catch()处理程序。

答案 1 :(得分:3)

  

代码不应该是等价的

他们是。

  

如果是这样,那么这是不是意味着只有在p1.then而不是承诺p1返回的承诺被解决拒绝后,才会调用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?

答案 2 :(得分:0)

此:

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