jQuery deferreds and promises - .then() vs .done()

后端 未结 10 1343
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 06:16

I\'ve been reading about jQuery deferreds and promises and I can\'t see the difference between using .then() & .done() for successful callbacks

相关标签:
10条回答
  • 2020-11-22 06:48

    deferred.done()

    adds handlers to be called only when Deferred is resolved. You can add multiple callbacks to be called.

    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    $.ajax(url).done(doneCallback);
    
    function doneCallback(result) {
        console.log('Result 1 ' + result);
    }
    

    You can also write above like this,

    function ajaxCall() {
        var url = 'http://jsonplaceholder.typicode.com/posts/1';
        return $.ajax(url);
    }
    
    $.when(ajaxCall()).then(doneCallback, failCallback);
    

    deferred.then()

    adds handlers to be called when Deferred is resolved, rejected or still in progress.

    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    $.ajax(url).then(doneCallback, failCallback);
    
    function doneCallback(result) {
        console.log('Result ' + result);
    }
    
    function failCallback(result) {
        console.log('Result ' + result);
    }
    
    0 讨论(0)
  • 2020-11-22 06:49

    then() always means it will be called in whatever case. But the parameters passing are different in different jQuery versions.

    Prior to jQuery 1.8, then() equals done().fail(). And all of the callback functions share same parameters.

    But as of jQuery 1.8, then() returns a new promise, and if it has return a value, it will be passed into the next callback function.

    Let's see the following example:

    var defer = jQuery.Deferred();
    
    defer.done(function(a, b){
                return a + b;
    }).done(function( result ) {
                console.log("result = " + result);
    }).then(function( a, b ) {
                return a + b;
    }).done(function( result ) {
                console.log("result = " + result);
    }).then(function( a, b ) {
                return a + b;
    }).done(function( result ) {
                console.log("result = " + result);
    });
    
    defer.resolve( 3, 4 );
    

    Prior to jQuery 1.8, the answer should be

    result = 3
    result = 3
    result = 3
    

    All result takes 3. And then() function always passes the same deferred object to the next function.

    But as of jQuery 1.8, the result should be:

    result = 3
    result = 7
    result = NaN
    

    Because the first then() function returns a new promise, and the value 7 (and this is the only parameter that will passed on)is passed to the next done(), so the second done() write result = 7. The second then() takes 7 as the value of a and takes undefined as the value of b, so the second then() returns a new promise with the parameter NaN, and the last done() prints NaN as its result.

    0 讨论(0)
  • 2020-11-22 06:51

    .done() has only one callback and it is the success callback

    .then() has both success and fail callbacks

    .fail() has only one fail callback

    so it is up to you what you must do... do you care if it succeeds or if it fails?

    0 讨论(0)
  • 2020-11-22 06:51

    .done() terminates the promise chain, making sure nothing else can attach further steps. This means that the jQuery promise implementation can throw any unhandled exception, since no one can possible handle it using .fail().

    In practical terms, if you do not plan to attach more steps to a promise, you should use .done(). For more details see why promises need to be done

    0 讨论(0)
  • 2020-11-22 06:52

    The callbacks attached to done() will be fired when the deferred is resolved. The callbacks attached to fail() will be fired when the deferred is rejected.

    Prior to jQuery 1.8, then() was just syntactic sugar:

    promise.then( doneCallback, failCallback )
    // was equivalent to
    promise.done( doneCallback ).fail( failCallback )
    

    As of 1.8, then() is an alias for pipe() and returns a new promise, see here for more information on pipe().

    success() and error() are only available on the jqXHR object returned by a call to ajax(). They are simple aliases for done() and fail() respectively:

    jqXHR.done === jqXHR.success
    jqXHR.fail === jqXHR.error
    

    Also, done() is not limited to a single callback and will filter out non-functions (though there is a bug with strings in version 1.8 that should be fixed in 1.8.1):

    // this will add fn1 to 7 to the deferred's internal callback list
    // (true, 56 and "omg" will be ignored)
    promise.done( fn1, fn2, true, [ fn3, [ fn4, 56, fn5 ], "omg", fn6 ], fn7 );
    

    Same goes for fail().

    0 讨论(0)
  • 2020-11-22 06:57

    Only use .then()

    These are the disadvantages of .done()

    • can not be chained
    • block resolve() call (all .done() handlers will be executed synchronous)
    • resolve() might get an exception from registered .done() handlers(!)
    • an exception in a .done() half-kills the deferred:
      • further .done() handlers will be silently skipped

    I thought temporarily that .then(oneArgOnly) always requires .catch() so that no exception gets silently ignored, but that is not true any more: the unhandledrejection event logs unhandled .then() exceptions on the console (as default). Very reasonable! No reason left to use .done() at all.

    Proof

    The following code snippet reveals, that:

    • all .done() handlers will be called synchronous at point of resolve()
      • logged as 1, 3, 5, 7
      • logged before the script falls through bottom
    • exception in a .done() influences resolve() caller
      • logged via catch around resolve()
    • exception breaks promise from further .done() resolution
      • 8 and 10 are not logged!
    • .then() has none of these problems
      • logged as 2, 4, 6, 9, 11 after thread turns idle
      • (snippet environment has no unhandledrejection is seems)

    Btw, exceptions from .done() can’t be properly caught: because of the synchronous pattern of .done(), the error is either thrown at the point of .resolve() (might be library code!) or at the .done() call which attaches the culprit if the deferred is already resolved.

    console.log('Start of script.');
    let deferred = $.Deferred();
    // deferred.resolve('Redemption.');
    deferred.fail(() => console.log('fail()'));
    deferred.catch(()=> console.log('catch()'));
    deferred.done(() => console.log('1-done()'));
    deferred.then(() => console.log('2-then()'));
    deferred.done(() => console.log('3-done()'));
    deferred.then(() =>{console.log('4-then()-throw');
        throw 'thrown from 4-then()';});
    deferred.done(() => console.log('5-done()'));
    deferred.then(() => console.log('6-then()'));
    deferred.done(() =>{console.log('7-done()-throw');
        throw 'thrown from 7-done()';});
    deferred.done(() => console.log('8-done()'));
    deferred.then(() => console.log('9-then()'));
    
    console.log('Resolving.');
    try {
        deferred.resolve('Solution.');
    } catch(e) {
        console.log(`Caught exception from handler
            in resolve():`, e);
    }
    deferred.done(() => console.log('10-done()'));
    deferred.then(() => console.log('11-then()'));
    console.log('End of script.');
    <script
    src="https://code.jquery.com/jquery-3.4.1.min.js"
    integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
    crossorigin="anonymous"
    ></script>

    0 讨论(0)
提交回复
热议问题