多个Ajax调用等待最后一个加载完成,然后执行。

19

我有多个ajax查询同时运行,希望它们等待最后一个返回,然后在所有ajax调用上运行成功处理程序。 以简化的例子为例:

$.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        //do something with data1
    }
});

....

$.ajax({//ajax call 2
    url:page2.php,
    success: function(data2){
        //do something with data2
    }
});
//consider more than just two concurrent requests
假设所有请求同时发送,由于它们是异步的,它们将在不同的时间返回。假设一个请求需要100ms返回,另一个请求需要3000ms返回。我显然不知道哪个请求会先返回或最后返回。它们都以某种方式更新DOM,并且我希望这些更改可以一次性地显示给观众。我该怎么做?
我所能想到的最好办法是将data1和data2保存为全局变量。然后有一个计数器变量,每当成功返回一次时计数。然后,在counter == TOTAL_NUM_REQUESTS的情况下调用像updateAll()这样的函数,然后运行所有全局变量并将它们放置在需要它们的位置上。但这似乎很混乱,容易出错。而且那将会是很多全局变量。
我理想中的解决方法是让成功处理程序返回时休眠,然后在我统计所有请求都已返回的条件下,向它们所有人发送唤醒消息,它们可以恢复执行。这似乎是最简洁的解决方案,但我不知道JavaScript中是否有此类功能。
大家有没有什么聪明的主意? 答案更新 感谢下面的Lee,我能够得到一个解决方案。我的解决方案看起来类似于他下面的解决方案。我构建了一个async变量列表,这些变量被分配给$.ajax调用。最初这些ajax调用的成功处理程序仍在被调用,所以我将它们删除并放到另一个函数中,随后由我编写的这个when块调用。我像这样将results传递给该函数:
var results = [];
results.push(async1);
results.push(async2);
... for all the results ...

$.when.apply(this, results).done(function() {
   for(var i=0;i<arguments.length;i++){
     dataobject=arguments[i][0]
     if(dataobject.variousattribute)
       mySuccessHandlerFirstGuy(dataobject)
     else if(dataobject.anotherattribute)
       mySuccessHandlerSecondGuy(dataobject)
       //etc ....
   }
};

要弄清楚 arguments 对象需要一些功夫。它是一个2D数组(列表的列表)。第一个索引表示与给定的 AJAX 请求相对应的返回对象。它看起来很有序,但最好让服务器返回您可以查找并编写 if/else 块的内容。然后,在该元素中,似乎有3个元素在该列表中。第一个是从服务器返回的值,即您想要的内容。第二个始终是字符串success,您可以使用它来检查调用是否成功。在该列表中的第三个元素似乎是初始请求(尽管我不确定)。这对我没有任何用处。

无论如何,我希望这可以帮助未来的某个人。再次感谢李指导我走向正确的方向。


可能是等待所有jQuery Ajax请求完成?的重复问题。 - cilf
3个回答

40

使用jQuery $.when()函数,在所有ajax调用完成时运行某些内容:

jQuery.when文档

var async1 = $.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        //do something with data1
    }
});

....

var async2 = $.ajax({//ajax call 2
    url:page2.php,
    success: function(data2){
        //do something with data2
    }
});

$.when(async2, async1).done(function(result2, result1) {
    ... do this when both are successful ...
});

回答问题时新增内容:

如果你有一堆ajax调用,你可以像这样使用 'apply':

var results = [];
results.push(async1);
results.push(async2);
... for all the results ...

$.when.apply(this, results).done(function() {
    ... use 'arguments' array to get results ...
});

“when”仅在其所有参数的顶部构建一个新的延迟,因此所有常规的延迟方法都可以使用。唯一棘手的部分是当第一个失败时它会失败,并且其他部分可能尚未完成。只有当所有操作都成功时,“done”才会执行。(就像“fail”是“或”运算,“done”的成功相当于“与”运算。) - Lee Meador
这看起来很有前途,“var async1”必须是全局范围的,对吗? - Landon
@Landon,“async1”类型的变量只需要对调用“when”的代码可见即可。它们可以在同一个函数中,也可以是全局的。 - Lee Meador
1
太好了,如果async2调用在运行动态次数的循环中怎么办?在这种情况下,我的代码中只有两个实例(变量)带有ajax函数,但我希望在执行之前进行5个ajax调用。那么在这种情况下,$.when的语法是什么? - Landon
2
我在上面的初始问题中扩展了解决方案,以供未来读者参考。感谢李的帮助。 - Landon
显示剩余4条评论

0

这个可以用非常类似于你已经提出的方式来完成。

首先,创建全局变量var sleep = truevar request_count = 0。然后启动一个定时器,每秒钟左右检查request_count与总请求数量的比较情况。接着,您的成功函数可以增加request_counter,然后开始循环,直到sleep == false。然后,当通过定时器运行的监视函数检测到request_count == TOTAL_REQUESTS时,它将设置sleep = false,此时所有成功函数都会继续进行。


你能澄清一下“循环直到sleep==false”的意思吗?像一个忙碌的循环吗?你是指类似于while(request_count<TOTAL_COUNT) busywork=1;这样的东西吗?我认为做一个忙碌的循环不是一个好主意。我错了吗? - Landon
1
是的,这基本上就是我所建议的。但你说得对,如果能避免那就更好了,而且看起来李的建议是正确的做法。 - Scott S

-1
一个简单的解决方案是使用PreloaderQ模块。

https://www.npmjs.com/package/preloaderq

使用方法如下

var preloader = new PreloaderQ()
preloader.setEmptyCallback(function(){
    //all tasks are finished
})

preloader.setFirstTaskCallback(function(){
    //started first task
})

preloader.enqueueTask('ajax1')

$.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        prealoader.dequeueTask('ajax1')
        //do something with data1
    }
});

preloader.enqueueTask('ajax2')

$.ajax({//ajax call 1
    url:page2.php,
    success: function(data2){
        prealoader.dequeueTask('ajax2')
        //do something with data2
    }
});

在两者完成后,emptycallback将被调用


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接