jQuery Deferred continue to resolve after fail handler

I have a promise that can resolve or reject. I want to do something specific in those cases, and then continue resolving the promise chain (essentially I want to "catch" the rejected promise, do something, then continue resolving).

Here's a functional snippet that shows the issue I'm running into :

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>

The pattern ...

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

... forms two (or more) branches, each then() being dependent only on def . Each branch possesses independent filtering power, but it is not exploited.

That is very different from ...

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

... which forms a single chain (without branching). The first then() is dependent on def , and the second then() dependent on the first then() . Here, the filtering power of the first then() is exploited by chaining another then() (and so on).

Therefore, you will get the expected output by converting the code in the question to the second pattern :

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();   

In a nutshell, that's what promise chaining is all about.

But don't forget about branching completely. In certain circumstances, it is essential. In this answer for example, batchRequests() returns _p , which can be further chained by the caller (that's one branch), but also forms its own private branch with _p.then(..., ...) . Don't worry if you can't follow it completely - it's fairly complicated - for now trust me that branching is an essential part of the solution.


The important part of the docs is

the deferred.then() method returns a new promise

You were however throwing away that return value, and did invoke the next .then(…) on the original def .

You will want to use

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

What your code is effectively doing is queuing two handlers (through the then functions). When the deferred is rejected it can no longer be altered.

When the Deferred is rejected, the first then handler fires writing your first console message.

The line return $.Deferred().resolve(); will create a new resolved Deferred but it is not "return"ed to your second then since that handler is already tied to the first Deferred instance.

Consequently, your second then handler will now fire also hitting the failFilter (the handler for when the Deferred is rejected).

One approach you could take is to pass a value when you reject (or resolve) your Deferred. You can then receive that response and take remedial action as shown in this (somewhat contrived) sample:

  <!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>
链接地址: http://www.djcxy.com/p/55460.html

上一篇: 如何获得承诺的价值?

下一篇: jQuery Deferred在失败处理程序后继续解析