jquery deferred - wait until two calls complete

假如想象 提交于 2021-02-17 19:31:06

问题


I am looking for a way to do a callback after two ajax calls completes:

$.when(
    call1(),
    call2()
).always(function() {
    // Here I want to be sure the two calls are done and to get their responses 
);

The catch is that one of the calls might fail. So, in my code the always will invoked without waiting to the other call.

How can I wait for both calls to done (success or failure)?


回答1:


Here is something that should do the trick:

$.whenAllDone = function() {
    var deferreds = [];
    var result = $.Deferred();

    $.each(arguments, function(i, current) {
        var currentDeferred = $.Deferred();
        current.then(function() {
            currentDeferred.resolve(false, arguments);
        }, function() {
            currentDeferred.resolve(true, arguments);
        });
        deferreds.push(currentDeferred);
    });

    $.when.apply($, deferreds).then(function() {
        var failures = [];
        var successes = [];

        $.each(arguments, function(i, args) {
            // If we resolved with `true` as the first parameter
            // we have a failure, a success otherwise
            var target = args[0] ? failures : successes;
            var data = args[1];
            // Push either all arguments or the only one
            target.push(data.length === 1 ? data[0] : args);
        });

        if(failures.length) {
            return result.reject.apply(result, failures);
        }

        return result.resolve.apply(result, successes);
    });

    return result;
}

Check out this Fiddle to see how it works.

Basically it waits for all Deferreds to finish no matter if they fail or not and collects all the results. If we have failures, the returned Deferred will fail with a list of all failures and resolve with all successes otherwise.




回答2:


It isn't pretty, but you could have a global "completed" variable for each ajax call to set when complete. Each call would also check whether both variables were set, and if so, call your always function.




回答3:


You can also nest the calls:

$.when(call1()).always(function(){
    $.when(call2()).always(function(){
        // Here I want to be sure the two calls are done and to get their responses
    });
});

But of course the two calls will become synchronous to each other.




回答4:


Daff's answer is good. There is only one problem. When there is only one deferred, things don't work.

The problem was inside jquery's when method.

jquery.when: function( subordinate /* , ..., subordinateN */ ) { ...

It has a line like:

// If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

And this changes the shape of the arguments, so I had to put it back to the common shape my code expects (i.e. the same shape when multiple deferreds are passed to whenAllDone)

const jqueryWhenUsesSubordinate = deferreds.length == 1;

const deferredArgs = jqueryWhenUsesSubordinate
    ? [[ arguments[ 0 ], arguments[ 1 ] ]]
    : arguments

$.each(deferredArgs, function (i, resolvedArgs) {
    var target = !resolvedArgs[0] ? failures : successes;
    var data = resolvedArgs[1];
    target.push(data.length === 1 ? data[0] : data);
});

Additionally, I changed the function signature to match more closely to Promise.allSettled in that it should take an array parameter of deferred objects, then instead of looping over arguments to set up the deferreds array, you loop over that parameter passed in.

This allows you to programmatically create a variable length of deferreds into an array and pass that into whenAllDone.



来源:https://stackoverflow.com/questions/15093145/jquery-deferred-wait-until-two-calls-complete

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!