首页 文章

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

提问于
浏览
0

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

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

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

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

3 回答

  • 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

    文档的重要部分是

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

    然而,你扔掉了那个返回值,并在原始的 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');
    });
    
  • 0

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

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

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

    因此,您的第二个 then 处理程序现在也将触发failFilter(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 .

相关问题