10

我正在寻找一种在两个 ajax 调用完成后进行回调的方法:

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

问题是其中一个调用可能会失败。所以,在我的代码中,总是会在不等待另一个调用的情况下调用。

如何等待两个调用都完成(成功或失败)?

4

4 回答 4

12

这是应该做的事情:

$.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;
}

看看这个 Fiddle看看它是如何工作的。

基本上它会等待所有 Deferreds 完成,无论它们是否失败并收集所有结果。如果我们有失败,则返回的 Deferred 将失败并列出所有失败,否则会以所有成功解决。

于 2013-02-26T16:29:29.430 回答
1

它并不漂亮,但是您可以为每个 ajax 调用设置一个全局“已完成”变量,以便在完成时设置。每次调用还将检查是否设置了两个变量,如果是,则调用您的 always 函数。

于 2013-02-26T15:39:44.720 回答
0

您还可以嵌套调用:

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

但是当然这两个调用会变得彼此同步。

于 2014-02-18T20:53:41.380 回答
0

达夫的回答很好。只有一个问题。当只有一个延迟时,事情就行不通了。

问题出在jquery的when方法中。

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

它有这样一行:

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

这改变了参数的形状,所以我不得不把它放回我的代码期望的通用形状(即当多个延迟传递给时相同的形状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);
});

此外,我更改了函数签名以更紧密地匹配Promise.allSettled,因为它应该采用延迟对象的数组参数,然后不是循环arguments设置deferreds数组,而是循环传入的参数。

这允许您以编程方式将可变长度的延迟创建到数组中并将其传递到whenAllDone.

于 2021-02-15T23:18:27.247 回答