【问题标题】:jQuery Deferred continue to resolve after fail handlerjQuery Deferred 在失败处理程序后继续解析
【发布时间】:2017-08-06 23:17:53
【问题描述】:

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

这是一个功能性的 sn-p,它显示了我遇到的问题:

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 deferred 的状态和值 功能[...]。这些过滤器函数可以返回一个新值 传递给 promise 的 .done() 或 .fail() 回调,或者它们 可以返回另一个可观察对象(Deferred、Promise 等) 将其已解决/拒绝的状态和值传递给承诺的 回调。

所以我不知道为什么这不起作用。我希望第一个失败处理程序中返回的已解决承诺允许承诺链的其余部分继续解决。

【问题讨论】:

标签: javascript jquery promise jquery-deferred deferred


【解决方案1】:

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

当 Deferred 被拒绝时,第一个 then 处理程序会触发写入您的第一条控制台消息。

return $.Deferred().resolve(); 行将创建一个新的已解析 Deferred,但它不会“返回”到您的第二个 then,因为该处理程序已经绑定到第一个 Deferred 实例。

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

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

  <!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>

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

【讨论】:

  • 对,我明白你在说什么,但是文档非常清楚地说明了成功/失败过滤器 “它们可以返回另一个可观察的对象(延迟、承诺等),这将将其已解决/已拒绝状态和值传递给承诺的回调。” 。所以我想知道的是为什么代码的行为不像文档所说的那样。
  • 请提供您获取该信息的链接。
【解决方案2】:

模式...

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(..., ...) 形成自己的私有分支。如果您不能完全遵循它,请不要担心 - 它相当复杂 - 现在相信我,分支是解决方案的重要组成部分。

【讨论】:

    【解决方案3】:

    文档的重要部分是

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

    但是,您丢弃了该返回值,并确实在原始 def 上调用了下一个 .then(…)

    你会想要使用

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-20
      • 2016-03-22
      相关资源
      最近更新 更多