0

I am trying to get my head around jQuery deferred/promises (using v1.8+).

From reading the documentation, a $.Deferred.then(...) method accepts a doneFilter and a failFilter as arguments to execute, and it returns a promise, which we can use to chain as follows:

function doDefer() {
    var $defer = $.Deferred();

    doSomethingAsynchronous(
        function () {
            $defer.resolve();
        },
        function () {
            $defer.reject();
        }
    );

    return $defer.promise();
}

doDefer()
    .then(function () {
        return;
    }, function () {
        // second `then` not called
    })
    .then(function (b) {
        // `b` is undefined, since first `then` returned without a value.
    }, function () {
        // fail end
    });

Two questions:

  1. Why does execution chain stop in doDefer(), if it hits the first fail function? As I understand, then returns a $.Deferred.promise() which is how the second then function is chained (and executes). The then doneFilter/function/callback in the first chain always returs(?) - so why/how does jQuery handle the failFilter differently?
  2. Why does then return a $.Deferred.promise(), yet done/always/fail, etc. return a $.Deferred? What use would this be (and why does it differ to then)?
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
keldar
  • 6,152
  • 10
  • 52
  • 82
  • No, it works just fine. I created a fiddle: https://jsfiddle.net/oy95nh47/1/ If you call "resolve", "done" is displayed twice. If you call "reject", "fail" is displayed twice. – Jodi Supporter Dec 17 '15 at 10:17
  • Hmm, it does now. Is there a way I can quit the chain if `fail` is called at any time? – keldar Dec 17 '15 at 10:30

3 Answers3

0

you can see this example, its work right:

function doDefer() {
    var $defer = $.Deferred();

    setTimeout( function(){
                doSomethingAsynchronous(
                    function ( value ) {
                        // func1 - value is a random number
                        $defer.resolve( value );
                    },
                    function () {
                        $defer.reject();
                    }
                );
            }, 1000 );

    return $defer.promise();
}

function doSomethingAsynchronous( func1, func2 ){
    // random number 0-10
    var random = Math.round( Math.random()*10 )
    if( random > 5 ){
        return func1.call( this, random );
    }else{
        return func2.call( this );
    }
}

doDefer().then(function ( value ) {
    // success
    // make double of random
    return value*2;
}, function () {
    // fail when random number < 5
})
.then(function (b) {
    // `b` is double of random number
    console.log(b);
}, function () {
    // fail end
});

I hope this help you

Khurram
  • 1
  • 2
0

Why does execution chain stop in doDefer(), if it hits the first fail function? As I understand, then returns a promise which is how the second then function is chained (and executes). The then done callback in the first chain always returs(?) - so why/how does jQuery handle the failFilter differently?

I'm not sure I understand you correctly, but there's two things you should know:

  • then(done, fail) only ever executes one of its callbacks, depending on whether the promise they were chained on is fulfilled or rejected.
  • error handling is completely broken in jQuery. The callback does get called as expected, but the promise returned by the .then(…) call does not behave Promises/A+ compliant. Wait for jQuery 3.0, use a proper promise library (on top of it), or learn to live with it.

Why does then return a $.Deferred.promise()

Because it's a promise that will resolve with the result of the respective callback. It's not a deferred that you can resolve yourself. See also What are the differences between Deferred, Promise and Future in JavaScript?

…yet done/always/fail, etc. return a $.Deferred?

They don't. They just return the object they were called on, regardless whether it was a promise or a deferred.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

the failHandler will stop execution like you said. The key is to look at the chain. A fail handler will only execute if the Promise it was chained to is rejected. Make sure you are looking at each link in the chain as a separate promise.

The chaining can be rewritten as follows.

dfd1 = doDefer();

dfd2 = dfd1.then( success1, fail1 );

dfd3 = dfd2.then( success2, fail2 );

in this example, success1 will only be called if dfd1 is resolved. If it is rejected, fail1 wil be called instead and the chain will stop.

Likewise, success2 will only be called if dfd2 is resolved (in other words, success1 returns a value or resolves a returned promise). The only way fail2 gets executed, is if dfd2 is rejected explicitly by success1 (error bubbling does not work in jQuery's implementation).

tl;dr - fails don't chain and as @Bergi said, error handling is broken in jQuery

thedarklord47
  • 3,183
  • 3
  • 26
  • 55