jQuery Deferred在失败处理程序后继续解析

时间:2017-03-15 23:58:24

标签: javascript jquery promise jquery-deferred deferred

我有一个可以解决或拒绝的承诺。我想在这些情况下做一些具体的事情,然后继续解决承诺链(基本上我想“抓住”被拒绝的承诺,做一些事情,然后继续解决)。

这是一个功能代码段,显示了我遇到的问题:

var def = $.Deferred();
def.then(
  function() {
    console.log('first success handler');
  },
  function() {
    console.log('first fail handler');
    return $.Deferred().resolve();
  }
);
def.then(
  function() {
    console.log('second success handler');
  },
  function() {
    console.log('second fail handler');
  }
);
def.done(function() {
  console.log('done handler');
});

def.reject();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

预期结果:

first fail handler
second success handler
done handler

目前的结果:

first fail handler
second fail handler

根据jQuery文档:

  

从jQuery 1.8开始,deferred.then()方法返回一个新的promise   可以过滤通过a延迟的状态和值   功能[...]。这些过滤器函数可以返回一个新值   传递给promise的.done()或.fail()回调,或者它们   可以返回另一个可观察对象(Deferred,Promise等)   将其解决/拒绝的状态和值传递给promise   回调。

所以我不知道为什么这不起作用。我期望在第一个失败处理程序中返回已解析的promise以允许其余的promise链继续解析。

3 个答案:

答案 0 :(得分:4)

模式......

var def = $.Deferred();
def.then(successHandler, errorHandler);
def.then(successHandler, errorHandler);
// etc.

...形成两个(或更多)分支,每个then()仅依赖于def。每个分支都具有独立的过滤能力,但没有被利用。

这与......非常不同。

var def = $.Deferred();
def.then(successHandler, errorHandler).then(successHandler, errorHandler);  // etc.

...形成单链(没有分支)。第一个then()取决于def,第二个then()取决于第一个then()。在这里,第一个then() 的过滤能力通过链接另一个then()(等等)来利用。

因此,您可以通过将问题中的代码转换为第二种模式来获得预期的输出:

var def = $.Deferred();

def.then(function() {
    console.log('first success handler');
}, function() {
    console.log('first fail handler'); // (1)
    return $.Deferred().resolve();
}).then(function() {
    console.log('second success handler'); // (2)
}, function() {
    console.log('second fail handler');
}).done(function() {
    console.log('done handler'); // (3)
});

def.reject();   

简而言之,这就是承诺链接的全部意义。

但不要忘记完全分支。在某些情况下,这是至关重要的。例如,在this answer中,batchRequests()返回_p,它可以被调用者(即一个分支)进一步链接,但也会与_p.then(..., ...)形成自己的私有分支。不要担心,如果你不能完全遵循它 - 它相当复杂 - 现在相信我,分支是解决方案的重要组成部分。

答案 1 :(得分:1)

文档的重要部分是

  

deferred.then()方法返回一个新的承诺

然而,您丢弃了该返回值,并调用了原始.then(…)上的下一个def

您需要使用

var p = $.Deferred().reject().promise();

p.then(function() {
    console.log('first success handler');
}, function() {
    console.log('first fail handler');
    return $.Deferred().resolve();
}).then(function() {
//^^^^^ chain directly on the result
    console.log('second success handler');
}, function() {
    console.log('second fail handler');
}).then(function() {
//^^^^^
  console.log('done handler');
});

答案 2 :(得分:0)

您的代码实际上正在排队的是两个处理程序(通过then函数)。当延期被拒绝时,它不能再被改变。

当Deferred被拒绝时,第一个then处理程序将触发编写第一个控制台消息。

return $.Deferred().resolve();将创建一个新的已解决的Deferred,但它不会&#34;返回&#34; ed到第二个then,因为该处理程序已经绑定到第一个Deferred实例。< / p>

因此,您的第二个then处理程序现在也将触发failFilter(Deferred被拒绝时的处理程序)。

您可以采取的一种方法是在拒绝(或解决)延期时传递值。然后,您可以收到该响应并采取补救措施,如此(有点人为的)样本所示:

&#13;
&#13;
  <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>

<div id="test"></div>

<script>
    var def = $.Deferred();
    def.then(
            function () {
                console.log('first success handler');
            },
            function (response) {
                console.log('first fail handler');
                console.log(response);
            }
    );
    def.then(
            function () {
                console.log('second success handler');
            },
            function (response) {
                if (response === 999) {
                    // do something to recover from earlier reject
                    console.log('recovery action initiated');
                } else {
                    console.log('second fail handler');
                }
            }
    );
    def.done(function () {
        console.log('done handler');
    });

    def.reject(999);
</script>

</body>
</html>
&#13;
&#13;
&#13;

我知道无法在两个then处理程序之间插入,它们只是同一个Deferred的两个排队处理程序。