Wait until all jQuery Ajax requests are done?
如何使一个函数等到另一个函数内完成所有jquery-ajax请求?
简而言之,在执行下一个请求之前,我需要等待所有Ajax请求完成。但是如何呢?
jquery现在为此定义了一个when函数。
它接受任意数量的延迟对象作为参数,并在所有这些对象解析后执行函数。
这意味着,如果您想启动(例如)四个Ajax请求,然后在它们完成时执行一个操作,您可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $.when(ajax1(), ajax2(), ajax3(), ajax4()).done(function(a1, a2, a3, a4){ // the code here will be executed when all four ajax requests resolve. // a1, a2, a3 and a4 are lists of length 3 containing the response text, // status, and jqXHR object for each of the four ajax calls respectively. }); function ajax1() { // NOTE: This function must return the value // from calling the $.ajax() method. return $.ajax({ url:"someUrl", dataType:"json", data: yourJsonData, ... }); } |
在我看来,它提供了一个干净清晰的语法,并避免涉及任何全局变量,如AjaxStart和AjaxStop,这可能会在页面开发时产生不必要的副作用。
如果您事先不知道需要等待多少Ajax参数(例如,您希望使用可变数量的参数),那么仍然可以完成,但只是稍微有点棘手。请参见传入一个延迟到$.when()的数组(或者jquery.when用变量数目的参数进行故障排除)。
如果需要对Ajax脚本等的失败模式进行更深入的控制,可以保存由
如果要等到文档中的所有Ajax请求都完成,不管它们有多少个,只需使用
1 2 3 | $(document).ajaxStop(function () { // 0 === $.active }); |
在这种情况下,不需要猜测一个应用程序中将来可能会完成多少请求。在某些情况下,Ajax请求可能是函数内部逻辑的一部分,这可能非常复杂(例如调用其他函数),在这种情况下,您可能不会等到所述函数完成其整个逻辑,而只是等待
这里的
同样,这个处理程序的目的是知道什么时候没有活动的
另外,如果您不介意使用ES6语法,那么您可以将
1 2 3 4 5 | Promise.all([ajax1(), ajax2()]).then(() => { // all requests finished successfully }).catch(() => { // all requests finished but one or more failed }) |
这里有趣的一点是,它既适用于
我找到了一个很好的答案,那正是我所寻找的。)
jquery半开队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //This handles the queues (function($) { var ajaxQueue = $({}); $.ajaxQueue = function(ajaxOpts) { var oldComplete = ajaxOpts.complete; ajaxQueue.queue(function(next) { ajaxOpts.complete = function() { if (oldComplete) oldComplete.apply(this, arguments); next(); }; $.ajax(ajaxOpts); }); }; })(jQuery); |
然后您可以像这样向队列添加Ajax请求:
1 2 3 4 5 6 7 8 | $.ajaxQueue({ url: 'page.php', data: {id: 1}, type: 'POST', success: function(data) { $('#status').html(data); } }); |
注意:以上答案使用的功能在编写此答案时不存在。我建议使用
-
您可能可以通过一个简单的计数信号量来实现它,尽管实现它的方式取决于您的代码。一个简单的例子是…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | var semaphore = 0, // counting semaphore for ajax requests all_queued = false; // bool indicator to account for instances where the first request might finish before the second even starts semaphore++; $.get('ajax/test1.html', function(data) { semaphore--; if (all_queued && semaphore === 0) { // process your custom stuff here } }); semaphore++; $.get('ajax/test2.html', function(data) { semaphore--; if (all_queued && semaphore === 0) { // process your custom stuff here } }); semaphore++; $.get('ajax/test3.html', function(data) { semaphore--; if (all_queued && semaphore === 0) { // process your custom stuff here } }); semaphore++; $.get('ajax/test4.html', function(data) { semaphore--; if (all_queued && semaphore === 0) { // process your custom stuff here } }); // now that all ajax requests are queued up, switch the bool to indicate it all_queued = true; |
如果您希望这样操作async:false,但不想锁定浏览器,则可以使用jquery队列完成相同的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var $queue = $(""); $queue.queue(function(){ $.get('ajax/test1.html', function(data) { $queue.dequeue(); }); }).queue(function(){ $.get('ajax/test2.html', function(data) { $queue.dequeue(); }); }).queue(function(){ $.get('ajax/test3.html', function(data) { $queue.dequeue(); }); }).queue(function(){ $.get('ajax/test4.html', function(data) { $queue.dequeue(); }); }); |
使用
例如,假设您正在加载…获取100个Ajax请求时的消息,您希望在加载后隐藏该消息。
从jquery文档:
1 2 3 | $("#loading").ajaxStop(function() { $(this).hide(); }); |
请注意,它将等待在该页上完成所有Ajax请求。
javascript是基于事件的,所以您不应该等待,而应该设置钩子/回调
您可能只需使用jquery.ajax的success/complete方法即可。
或者可以使用.ajaxComplete:
1 2 3 4 5 6 | $('.log').ajaxComplete(function(e, xhr, settings) { if (settings.url == 'ajax/test.html') { $(this).text('Triggered ajaxComplete handler.'); //and you can do whatever other processing here, including calling another function... } }); |
尽管您应该发布一个伪代码,说明如何调用您的Ajax请求,以便更加精确…
一个小小的解决方法是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | // Define how many Ajax calls must be done var ajaxCalls = 3; var counter = 0; var ajaxCallComplete = function() { counter++; if( counter >= ajaxCalls ) { // When all ajax calls has been done // Do something like hide waiting images, or any else function call $('*').css('cursor', 'auto'); } }; var loadPersons = function() { // Show waiting image, or something else $('*').css('cursor', 'wait'); var url = global.ctx + '/loadPersons'; $.getJSON(url, function(data) { // Fun things }) .complete(function() { **ajaxCallComplete();** }); }; var loadCountries = function() { // Do things var url = global.ctx + '/loadCountries'; $.getJSON(url, function(data) { // Travels }) .complete(function() { **ajaxCallComplete();** }); }; var loadCities = function() { // Do things var url = global.ctx + '/loadCities'; $.getJSON(url, function(data) { // Travels }) .complete(function() { **ajaxCallComplete();** }); }; $(document).ready(function(){ loadPersons(); loadCountries(); loadCities(); }); |
希望是有用的…
正如前面提到的其他答案,您可以使用
1 2 3 | $(document).ajaxStop(function() { // This function will be triggered every time an ajax is requested and completed }); |
但是,如果您希望针对特定的
1 2 3 4 5 6 7 8 9 10 11 12 13 | $.ajax({ type:"POST", url:"someUrl", success: function(data) { // This function will be triggered when ajax returns a 200 status code (success) }, complete: function() { // This function will be triggered always, when ajax request is completed, even it fails/returns other status code }, error: function() { // This will be triggered when ajax request fail. } }); |
基于@bbonifield answer,我编写了一个实用函数,这样信号量逻辑就不会在所有Ajax调用中传播。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function untilAjax(ajaxObjs, fn) { if (!ajaxObjs || !fn) { return; } var ajaxCount = ajaxObjs.length, succ = null; for (var i = 0; i < ajaxObjs.length; i++) { //append logic to invoke callback function once all the ajax calls are completed, in success handler. succ = ajaxObjs[i]['success']; ajaxObjs[i]['success'] = function(data) { //modified success handler if (succ) { succ(data); } ajaxCount--; if (ajaxCount == 0) { fn(); //modify statement suitably if you want 'this' keyword to refer to another object } }; $.ajax(ajaxObjs[i]); //make ajax call succ = null; }; |
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function doSomething() { // variable declarations untilAjax([{ url: 'url2', dataType: 'json', success: function(data) { //do something with success data } }, { url: 'url1', dataType: 'json', success: function(data) { //do something with success data } }, { url: 'url2', dataType: 'json', success: function(response) { //do something with success data } }], function() { // logic after all the calls are completed. }); } |
为了进一步阐述亚历克斯的答案,我举了一个例子,其中有可变的论点和承诺。我想通过Ajax加载图像,并在它们全部加载后将它们显示在页面上。
为此,我使用了以下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | let urlCreator = window.URL || window.webkitURL; // Helper function for making ajax requests let fetch = function(url) { return $.ajax({ type:"get", xhrFields: { responseType:"blob" }, url: url, }); }; // Map the array of urls to an array of ajax requests let urls = ["https://placekitten.com/200/250","https://placekitten.com/300/250"]; let files = urls.map(url => fetch(url)); // Use the spread operator to wait for all requests $.when(...files).then(function() { // If we have multiple urls, then loop through if(urls.length > 1) { // Create image urls and tags for each result Array.from(arguments).forEach(data => { let imageUrl = urlCreator.createObjectURL(data[0]); let img = `<img src=${imageUrl}>`; $("#image_container").append(img); }); } else { // Create image source and tag for result let imageUrl = urlCreator.createObjectURL(arguments[0]); let img = `<img src=${imageUrl}>`; $("#image_container").append(img); } }); |
更新后可用于单个或多个URL:https://jsfiddle.net/euypj5w9/
当所有Ajax加载完成时,我将使用大小检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | function get_ajax(link, data, callback) { $.ajax({ url: link, type:"GET", data: data, dataType:"json", success: function (data, status, jqXHR) { callback(jqXHR.status, data) }, error: function (jqXHR, status, err) { callback(jqXHR.status, jqXHR); }, complete: function (jqXHR, status) { } }) } function run_list_ajax(callback){ var size=0; var max= 10; for (let index = 0; index < max; index++) { var link = 'http://api.jquery.com/ajaxStop/'; var data={i:index} get_ajax(link,data,function(status, data){ console.log(index) if(size>max-2){ callback('done') } size++ }) } } run_list_ajax(function(info){ console.log(info) }) |
1 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"> |
如果您需要简单的东西;一次性完成回调
1 2 3 4 5 6 7 8 9 10 | //multiple ajax calls above var callback = function () { if ($.active !== 0) { setTimeout(callback, '500'); return; } //whatever you need to do here //... }; callback(); |
也可以使用async.js。
我认为它比$.when更好,因为您可以合并各种不支持开箱即用承诺的异步调用,如超时、sqllite调用等,而不仅仅是Ajax请求。
如果从头开始,我强烈建议使用$.when()。
尽管这个问题的答案超过了百万,但我仍然没有找到对我的案件有用的任何东西。假设您必须处理现有的代码库,已经进行了一些Ajax调用,并且不想引入承诺的复杂性和/或重做整个事情。
我们可以很容易地利用jquery
科德森
我的解决方案的优点是:
很明显回调到底依赖于什么
函数
triggerNowOrOnLoaded 不关心数据是否已经加载,或者我们还在等待它将其插入现有代码非常容易
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | $(function() { // wait for posts to be loaded triggerNowOrOnLoaded("posts", function() { var $body = $("body"); var posts = $body.data("posts"); $body.append("Posts:" + posts.length +""); }); // some ajax requests $.getJSON("https://jsonplaceholder.typicode.com/posts", function(data) { $("body").data("posts", data).trigger("posts"); }); // doesn't matter if the `triggerNowOrOnLoaded` is called after or before the actual requests $.getJSON("https://jsonplaceholder.typicode.com/users", function(data) { $("body").data("users", data).trigger("users"); }); // wait for both types triggerNowOrOnLoaded(["posts","users"], function() { var $body = $("body"); var posts = $body.data("posts"); var users = $body.data("users"); $body.append("Posts:" + posts.length +" and Users:" + users.length +""); }); // works even if everything has already loaded! setTimeout(function() { // triggers immediately since users have been already loaded triggerNowOrOnLoaded("users", function() { var $body = $("body"); var users = $body.data("users"); $body.append("Delayed Users:" + users.length +""); }); }, 2000); // 2 seconds }); // helper function function triggerNowOrOnLoaded(types, callback) { types = $.isArray(types) ? types : [types]; var $body = $("body"); var waitForTypes = []; $.each(types, function(i, type) { if (typeof $body.data(type) === 'undefined') { waitForTypes.push(type); } }); var isDataReady = waitForTypes.length === 0; if (isDataReady) { callback(); return; } // wait for the last type and run this function again for the rest of the types var waitFor = waitForTypes.pop(); $body.on(waitFor, function() { // remove event handler - we only want the stuff triggered once $body.off(waitFor); triggerNowOrOnLoaded(waitForTypes, callback); }); } |
1 2 3 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> <body>Hi!</body> |
我找到了简单的方法,它使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function waitReq(id) { jQuery.ajax( { type: 'POST', url: ajaxurl, data: { "page": id }, success: function(resp) { ........... // check array length if not"0" continue to use next array value if(ids.length) { waitReq(ids.shift()); // 2 ) }, error: function(resp) { .................... if(ids.length) { waitReq(ids.shift()); ) } }); } var ids = [1, 2, 3, 4, 5]; // shift() = delete first array value (then print) waitReq(ids.shift()); // print 1 |
我遇到了这个问题,并创建了一个通用插件jquery_计数器来解决它:https://bitback.org/stxnext/jquery_计数器/
看看我的解决方案:
1.将此函数(和变量)插入到您的javascript文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | var runFunctionQueue_callback; function runFunctionQueue(f, index, callback) { var next_index = index + 1 if (callback !== undefined) runFunctionQueue_callback = callback; if (f[next_index] !== undefined) { console.log(index + ' Next function avalaible -> ' + next_index); $.ajax({ type: 'GET', url: f[index].file, data: (f[index].data), complete: function() { runFunctionQueue(f, next_index); } }); } else { console.log(index + ' Last function'); $.ajax({ type: 'GET', url: f[index].file, data: (f[index].data), async: false, complete: runFunctionQueue_callback }); } } |
2.用您的请求构建一个数组,如下所示:
1 2 3 4 5 6 | var f = [ {file: 'file_path', data: {action: 'action', data: 'any_data}}, {file: 'file_path', data: {action: 'action', data: 'any_data}}, {file: 'file_path', data: {action: 'action', data: 'any_data}}, {file: 'file_path', data: {action: 'action', data: 'any_data}} ]; |
3.创建回调函数:
1 2 3 | function Function_callback() { alert('done'); } |
4.用参数调用runfunctionqueue函数:
1 2 3 4 | runFunctionQueue(f, 0, QuestionInsert_callback); // first parameter: array with requests data // second parameter: start from first request // third parameter: the callback function |
jquery允许您指定是否希望Ajax请求是异步的。您可以简单地使Ajax请求同步,然后剩下的代码在返回之前不会执行。
例如:
1 2 3 4 | jQuery.ajax({ async: false, //code }); |
我的解决方案如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var request; ... 'services': { 'GetAddressBookData': function() { //This is the primary service that loads all addressbook records request = $.ajax({ type:"POST", url:"Default.aspx/GetAddressBook", contentType:"application/json;", dataType:"json" }); }, ... 'apps': { 'AddressBook': { 'data':"", 'Start': function() { ...services.GetAddressBookData(); request.done(function(response) { trace("ajax successful"); ..apps.AddressBook.data = response['d']; ...apps.AddressBook.Filter(); }); request.fail(function(xhr, textStatus, errorThrown) { trace("ajax failed -" + errorThrown); }); |
工作很好。我尝试过很多不同的方法,但我发现这是最简单和最可重用的方法。希望它有帮助
亚历克斯给出的解决方案很好。相同的概念,但使用方式略有不同(当提前不知道呼叫数时)
http://garbageoverflow.blogspot.com/2014/02/wait-for-n-or-multiple-or-unknown.html
试试这边。在Java脚本函数中生成一个循环,直到Ajax调用完成为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | function getLabelById(id) { var label = ''; var done = false; $.ajax({ cache: false, url:"YourMvcActionUrl", type:"GET", dataType:"json", async: false, error: function (result) { label='undefined'; done = true; }, success: function (result) { label = result.Message; done = true; } }); //A loop to check done if ajax call is done. while (!done) { setTimeout(function(){ },500); // take a sleep. } return label; } |