多个ajax调用的jQuery回调


132

我想在click事件中进行三个Ajax调用。每个ajax调用都会执行不同的操作,并返回最终回调所需的数据。调用本身并不相互依赖,它们可以同时进行,但是我希望在所有三个都完成后进行最后的回调。

$('#button').click(function() {
    fun1();
    fun2();
    fun3();
//now do something else when the requests have done their 'success' callbacks.
});

var fun1= (function() {
    $.ajax({/*code*/});
});
var fun2 = (function() {
    $.ajax({/*code*/});
});
var fun3 = (function() {
    $.ajax({/*code*/});
});


Answers:


112

这是我写的一个回调对象,您可以设置单个回调以在全部完成后触发,也可以让每个回调都有自己的回调并在全部完成后触发它们:

注意

从jQuery 1.5+开始,您可以按照另一个答案中所述使用deferred方法:

  $.when($.ajax(), [...]).then(function(results){},[...]);

此处延迟的示例

对于jQuery <1.5,下面的方法将起作用,或者如果您需要在未知时间触发ajax调用,如此处所示,有两个按钮:单击两个按钮后触发

[用法]

对于单一的回调完成后:工作实例

// initialize here
var requestCallback = new MyRequestsCompleted({
    numRequest: 3,
    singleCallback: function(){
        alert( "I'm the callback");
    }
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});

每个都完成时都有自己的回调:工作示例

//initialize 
var requestCallback = new MyRequestsCompleted({
    numRequest: 3
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the first callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the second callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the third callback');
        });
    }
});

[代码]

var MyRequestsCompleted = (function() {
    var numRequestToComplete, requestsCompleted, callBacks, singleCallBack;

    return function(options) {
        if (!options) options = {};

        numRequestToComplete = options.numRequest || 0;
        requestsCompleted = options.requestsCompleted || 0;
        callBacks = [];
        var fireCallbacks = function() {
            alert("we're all complete");
            for (var i = 0; i < callBacks.length; i++) callBacks[i]();
        };
        if (options.singleCallback) callBacks.push(options.singleCallback);

        this.addCallbackToQueue = function(isComplete, callback) {
            if (isComplete) requestsCompleted++;
            if (callback) callBacks.push(callback);
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.requestComplete = function(isComplete) {
            if (isComplete) requestsCompleted++;
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.setCallback = function(callback) {
            callBacks.push(callBack);
        };
    };
})();

1
感谢出色的代码示例!我想我可以在其余的过程中迷路了。再次感谢!
MisterIsaak 2010年

没有问题,很高兴它有所帮助!我有点被它带走了:P仍然对此有更多的想法。我计划将其更新到不必在初始化时指定多个请求的位置。
10

很好,我希望你能做到!非常有趣,我建议添加该选项以添加其他回调。我做到了,它非常适合我想做的事情。再次感谢!
MisterIsaak 2010年

我没有,但是忘记将其添加到用例中:P用于setCallback向队列中添加更多内容。尽管现在我考虑了它……我应该称呼它addCallback或类似的东西……而不是仅仅设置它,因为它已经添加到队列中了
Subhaze 2010年

singleCallback第二行的变量是否不必要?还是我错过了什么?
nimcap 2012年

158

看来您已经找到了答案,但是我认为这里值得一提的是可以大大简化您的代码。jQuery $.when在v1.5中引入了。看起来像:

$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //this callback will be fired once all ajax calls have finished.
});

没看到这里提到的内容,希望对您有所帮助。


6
感谢您的回答,这绝对是我要走的路。它使用jQuery,紧凑,简短且富有表现力。
nimcap 2012年

2
Subhaze的答案很好,但实际上,这是正确的答案。
Molomby 2012年

9
我同意,当您使用jQuery 1.5+时,这是一个很好的解决方案,但是在回答之时,延迟功能不可用。:(
subhaze 2012年

13

我自己没有看到任何物体的需求。简单的变量是整数。启动请求时,增加数字。完成后,将其递减。为零时,没有任何请求在进行中,因此您已完成。

$('#button').click(function() {
    var inProgress = 0;

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what's in here when all requests have completed.
        }
    };

    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
});

这非常简单并且可以完美地工作...您能介意更好地解释if(--inProgress)中发生的情况吗?它应该控制变量inProgress == 0并减去一个单位,但是我没有紧凑的语法...
Pitto

1
@Pitto:糟糕...实际上,那里存在逻辑错误,应该是if (!--inProgress)。正确的版本类似于inProgress = inProgress - 1; if (inProgress == 0) { /* do stuff */ }。精简形式将前缀减量运算符(--和否定运算符(!)组合起来if,如果减量inProgress结果为inProgress == 0,则计算结果为“ true”(因此执行该块),否则,计算结果为“ false”(因此不执行任何操作)。
马特

简直太糟糕了!感谢您的时间和耐心等待。
皮托2015年

@Pitto:嗯..您可以链接到您使用的代码吗?它对我有用
马特

10

值得注意的是,由于$.when期望将所有ajax请求都作为顺序参数(而不是数组)$.when.apply()因此通常会这样使用:

// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
    return $.ajax({
        method: 'GET',
        url: 'thing/' + thing.id
    });
});
$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
    // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
    var responseArgsArray = Array.prototype.slice.call(this, arguments);

});

这是因为$.when接受这样的参数

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

而且不是这样的:

$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);

很好的回应。现在有很多好的承诺库,其中大多数都有all方法或类似的东西,但是我喜欢地图的概念。真好
格雷格

2
是的,我几乎总是通过在$ .when上应用一系列请求来使用它,而在这里的解决方案中没有发现它,因此只需在此处添加一个好例子即可。
科里·丹尼尔森

4

我喜欢hvgotcodes的想法。我的建议是添加一个通用增量器,将完成的数字与所需的数字进行比较,然后运行最终的回调。这可以内置到最终的回调中。

var sync = {
 callbacksToComplete = 3,
 callbacksCompleted = 0,
 addCallbackInstance = function(){
  this.callbacksCompleted++;
  if(callbacksCompleted == callbacksToComplete) {
   doFinalCallBack();
  }
 }
};

[编辑以反映名称更新。]


^同意,有助于更好地了解我的实际需求。
MisterIsaak 2010年

3

编辑-也许最好的选择是创建一个服务端点,该端点执行三个请求所做的所有工作。这样,您只需要执行一个请求,所有数据就可以放在响应中。如果发现您一次又一次地执行相同的3个请求,则可能要走这条路线。在服务器上设置立面服务通常是一个不错的设计决定,该服务将通常使用的较小的服务器操作集中在一起。只是个主意。


一种方法是在ajax调用之前在点击处理程序中创建一个“同步”对象。就像是

var sync = {
   count: 0
}

同步将自动绑定到成功调用的范围(关闭)。在成功处理程序中,您可以增加计数,如果计数为3,则可以调用另一个函数。

或者,您可以执行类似

var sync = {
   success1Complete: false,
   ...
   success3Complete: false,
}

当每次成功执行时,都会将同步中的值更改为true。在继续之前,您必须检查同步以确保所有三个都正确。

请注意您的xhrs之一未返回成功的情况-您需要考虑到这一点。

还有一种选择是始终在成功处理程序中调用final函数,并使其访问sync选项以确定是否实际执行任何操作。但是,您需要确保同步在该功能的范围内。


由于几个原因,我更喜欢您的第一个建议。每个请求都有不同的目的,因此出于这个原因,我希望将它们分开。另外,这些请求实际上是在一个函数中,因为我在其他地方使用了它们,因此,我尽可能尝试保持模块化设计。谢谢你的帮助!
MisterIsaak 2010年

@Jisaak,是的,在服务器上放置一种方法来处理此问题可能对您没有意义。但是可以在服务器上设置外观。您谈论模块化,创建一种处理所有三件事的方法是模块化。
hvgotcodes 2010年


0

我从本页的答案中得到了一些很好的提示。我对它做了一些修改以供使用,并认为可以分享。

// lets say we have 2 ajax functions that needs to be "synchronized". 
// In other words, we want to know when both are completed.
function foo1(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo1');
            callback();               
        }
    });
}

function foo2(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo2');
            callback();
        }
    });
}

// here is my simplified solution
ajaxSynchronizer = function() {
    var funcs = [];
    var funcsCompleted = 0;
    var callback;

    this.add = function(f) {
        funcs.push(f);
    }

    this.synchronizer = function() {
        funcsCompleted++;
        if (funcsCompleted == funcs.length) {
            callback.call(this);
        }
    }

    this.callWhenFinished = function(cb) {
        callback = cb;
        for (var i = 0; i < funcs.length; i++) {
            funcs[i].call(this, this.synchronizer);
        }
    }
}

// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
    alert('All done!');
}

// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);

这里有一些限制,但就我而言,还可以。我还发现,对于更高级的内容,它还有一个AOP插件(用于jQuery)可能会有用:http : //code.google.com/p/jquery-aop/


0

我今天遇到了这个问题,这是我天真的尝试,然后才看了接受的答案。

<script>
    function main() {
        var a, b, c
        var one = function() {
            if ( a != undefined  && b != undefined && c != undefined ) {
                alert("Ok")
            } else {
                alert( "¬¬ ")
            }
        }

        fakeAjaxCall( function() {
            a = "two"
            one()
        } )
        fakeAjaxCall( function() {
            b = "three"
            one()
        } )
        fakeAjaxCall( function() {
            c = "four"
            one()
        } )
    }
    function fakeAjaxCall( a ) {
        a()
    }
    main()
</script>

0

它不是jquery(看来jquery有一个可行的解决方案),但作为另一个选择...。

我在使用SharePoint Web服务时遇到了类似的问题-您通常需要从多个来源提取数据来为单个流程生成输入。

为了解决这个问题,我将这种功能嵌入到我的AJAX抽象库中。您可以轻松定义一个请求,该请求将在完成时触发一组处理程序。但是,可以使用多个http调用定义每个请求。这是组件(和详细的文档):

DepressedPress.com上的DPAJAX

这个简单的示例创建了一个包含三个调用的请求,然后将该信息按照调用顺序传递给单个处理程序:

    // The handler function 
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) }; 

    // Create the pool 
myPool = DP_AJAX.createPool(); 

    // Create the request 
myRequest = DP_AJAX.createRequest(AddUp); 

    // Add the calls to the request 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]); 

    // Add the request to the pool 
myPool.addRequest(myRequest); 

请注意,与许多其他解决方案(包括,我相信jquery中的“ when”解决方案)不同,该方法不会强制执行所调用的单线程-每个调用仍将根据环境允许的速度(或缓慢)运行但只有全部处理完毕后,才会调用单个处理程序。如果您的服务有点麻烦,它还支持设置超时值和重试尝试。

我发现它非常有用(而且从代码角度看也非常简单)。不再需要链接,不再需要计数呼叫并保存输出。只需“设置并忘记它”。


0

好的,这很旧,但是请让我贡献我的解决方案:)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

它也可用于具有回调的函数。设置syncCount并在每个操作的回调中调用函数sync(...)。


0

我发现了一种更简单的方法,不需要额外的安排队列的方法。

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP(ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP(ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

-1
async   : false,

默认情况下,所有请求都是异步发送的(即默认情况下设置为true)。如果需要同步请求,请将此选项设置为false。跨域请求和dataType: "jsonp"请求不支持同步操作。请注意,同步请求可能会暂时锁定浏览器,从而在请求处于活动状态时禁用任何操作。作为jQuery的1.8,使用async: falsejqXHR$.Deferred)是已经过时; 您必须使用成功/错误/完成回调选项,而不要使用jqXHR对象的相应方法(例如jqXHR.done()或不推荐使用)jqXHR.success()


这不是一个适当的解决方案。您绝对不应发出同步Ajax请求。
user128216 '16

-2
$.ajax({type:'POST', url:'www.naver.com', dataType:'text', async:false,
    complete:function(xhr, textStatus){},
    error:function(xhr, textStatus){},
    success:function( data ){
        $.ajax({type:'POST', 
            ....
            ....
            success:function(data){
                $.ajax({type:'POST',
                    ....
                    ....
            }
        }
    });

对不起,但是我无法解释我的意思。我是韩国人,即使英语也不会说一个字。但我认为您可以轻松理解。


不应该这样使用ajax。回调地狱..!
traeper '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.