等到所有jQuery Ajax请求都完成?

如何让一个函数等到所有jQuery Ajax请求都在另一个函数中完成?

简而言之,在执行下一个Ajax请求之前,我需要等待所有Ajax请求完成。

682877 次浏览

注:上述答案使用了编写此答案时不存在的功能。我建议使用jQuery.when()而不是这些方法,但我将答案留给历史目的。

-

您可能可以使用简单的计数信号量,尽管您如何实现它取决于您的代码。一个简单的例子是这样的…

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队列完成同样的事情。

var $queue = $("<div/>");
$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();
});
});

jQuery允许您指定是否希望ajax请求是异步的。您可以简单地使ajax请求同步,然后其余代码在它们返回之前不会执行。

例如:

jQuery.ajax({
async: false,
//code
});

JavaScript是基于事件的,所以你永远不应该等待,而应该设置钩子/回调

您可以使用jquery.ajax的成功/完整方法

或者你可以使用. ajax完成

$('.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请求是如何被更精确地调用的…

我发现了一个好答案gnarf我自己,这正是我正在寻找的:)

jQuery ajaxQueue

//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请求添加到队列中:

$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});

看看我的解决方案:

1.将此函数(和变量)插入您的javascript文件:

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.用你的请求构建一个数组,如下所示:

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.创建回调函数:

function Function_callback() {
alert('done');
}

4.使用参数调用runFunctionQueue函数:

runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function

使用ajaxStop事件。

例如,假设您在获取100个ajax请求时有一条加载…消息,并且您希望在加载后隐藏该消息。

从jQuerydoc

$("#loading").ajaxStop(function() {
$(this).hide();
});

请注意,它将等待在该页面上完成所有ajax请求。

在@BBonifield回答的基础上,我编写了一个实用函数,以便信号量逻辑不会在所有ajax调用中传播。

untilAjax是一个实用函数,它在所有ajax调用完成时调用回调函数。

ajaxObjs是ajax设置对象[http://api.jquery.com/jQuery.ajax/]的数组。

fn是回调函数

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

示例:doSomething函数使用untilAjax

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

我的解决方案如下

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);
});

工作得很好。我试过很多不同的方法,但我发现这是最简单,最可重用的。希望它有帮助

jQuery现在为此定义了一个当函数

它接受任意数量的Deferred对象作为参数,并在所有对象都解析时执行一个函数。

这意味着,如果你想启动(例如)四个ajax请求,然后在它们完成后执行一个操作,你可以这样做:

$.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参数(即你想使用可变数量的参数),它仍然可以完成,但只是有点棘手。参见将Deferreds数组传入$. time()(也许还有使用可变数量的参数进行故障排除时)。

如果您需要更深入地控制ajax脚本的失败模式等,您可以保存.when()返回的对象-它是一个包含所有原始ajax查询的jQueryPromise对象。您可以在其上调用.then().fail()来添加详细的成功/失败处理程序。

一个小解决方案是这样的:

// 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();
});

希望是有用的……

如果您想知道当所有ajax请求在您的文档中已完成,无论存在多少个请求,只需以这种方式使用$.ajaxStop事件:

$(document).ajaxStop(function () {
// 0 === $.active
});

在这种情况下,您不需要猜测应用程序中发生了多少请求,这些请求可能会在未来完成,也不需要挖掘复杂逻辑的函数或查找哪些函数正在执行HTTP(S)请求。

$.ajaxStop在这里也可以绑定到任何HTML节点

可能会被requst修改。


更新时间:
如果你想坚持使用ES语法,那么你可以对已知的ajax方法使用Promise.all

Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})

这里有趣的一点是,它适用于Promises$.ajax请求。

下面是jsFiddle演示。


更新2:
使用异步/等待语法的最新版本:

try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }

如果你需要一些简单的东西;一旦完成回调

        //multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();

试试这个方法。在java脚本函数中循环等待ajax调用完成。

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

我发现简单的方法,它使用shift()

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

您也可以使用async.js

我认为它比$.时更好,因为您可以合并各种不支持开箱即用的承诺的异步调用,如超时、SqlLite调用等,而不仅仅是ajax请求。

我强烈建议使用什么时候(),如果你从头开始。

即使这个问题有超过一百万个答案,我仍然没有找到任何对我的案例有用的东西。假设您必须处理现有的代码库,已经进行了一些ajax调用,并且不想引入Promise的复杂性和/或重做整个事情。

我们可以轻松利用jQuery.data.on.trigger函数,这些函数一直是jQuery的一部分。

代码

我的解决方案的好处是:

  • 很明显回调到底取决于什么

  • 函数triggerNowOrOnLoaded不关心数据是否已经加载或我们仍在等待它

  • 将其插入现有代码非常容易

$(function() {


// wait for posts to be loaded
triggerNowOrOnLoaded("posts", function() {
var $body = $("body");
var posts = $body.data("posts");


$body.append("<div>Posts: " + posts.length + "</div>");
});




// 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("<div>Posts: " + posts.length + " and Users: " + users.length + "</div>");
});


// 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("<div>Delayed Users: " + users.length + "</div>");
});


}, 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);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<body>Hi!</body>

$.when不适合我,callback(x)而不是return x的工作方式如下所述:https://stackoverflow.com/a/13455253/10357604

当所有Ajax加载完成时,我正在使用尺寸检查

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)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>

为了扩展Alex的回答,我有一个带有可变参数和Promise的示例。我想通过ajax加载图像,并在它们全部加载后将它们显示在页面上。

为此,我使用了以下方法:

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/

正如其他答案提到的,您可以使用ajaxStop()等待所有ajax请求完成。

$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});

如果您想为特定的ajax()请求执行此操作,最好在特定的ajax请求中使用complete()方法:

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


但是,如果您只需要等待几个和某些ajax请求完成?使用精彩的javascript承诺等待,直到你想等待的这些ajax完成。我做了一个简短、简单且可读的例子来向你展示Promise如何与ajax一起工作。
请看下一个例子。我使用setTimeout来阐明这个例子。

// Note:
// resolve() is used to mark the promise as resolved
// reject() is used to mark the promise as rejected


$(document).ready(function() {
$("button").on("click", function() {


var ajax1 = new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "https://miro.medium.com/max/1200/0*UEtwA2ask7vQYW06.png",
xhrFields: { responseType: 'blob'},
success: function(data) {
setTimeout(function() {
$('#image1').attr("src", window.URL.createObjectURL(data));
resolve(" Promise ajax1 resolved");
}, 1000);
},
error: function() {
reject(" Promise ajax1 rejected");
},
});
});


var ajax2 = new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512.png",
xhrFields: { responseType: 'blob' },
success: function(data) {
setTimeout(function() {
$('#image2').attr("src", window.URL.createObjectURL(data));
resolve(" Promise ajax2 resolved");
}, 1500);
},
error: function() {
reject(" Promise ajax2 rejected");
},
});
});


var ajax3 = new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png",
xhrFields: { responseType: 'blob' },
success: function(data) {
setTimeout(function() {
$('#image3').attr("src", window.URL.createObjectURL(data));
resolve(" Promise ajax3 resolved");
}, 2000);
},
error: function() {
reject(" Promise ajax3 rejected");
},
});
});
        

Promise.all([ajax1, ajax2, ajax3]).then(values => {
console.log("We waited until ajax ended: " + values);
console.log("My few ajax ended, lets do some things!!")
}, reason => {
console.log("Promises failed: " + reason);
});
        

// Or if you want wait for them individually do it like this
// ajax1.then(values => {
//    console.log("Promise 1 resolved: " + values)
// }, reason => {
//     console.log("Promise 1 failed: " + reason)
// });
});


});
img {
max-width: 200px;
max-height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Make AJAX request</button>
<div id="newContent">
<img id="image1" src="">
<img id="image2" src="">
<img id="image3" src="">
</div>

这对我很有用 很简单

return $.ajax({
type: 'POST',
url: urlBaseUrl
data: {someData:someData},
dataType: "json",
success: function(resultData) {
}
});

下面的解决方案对我来说很有用

$.when(master.GetStateByName(stateName)).done(function(response) {
if (response) {


}
});


GetStateByName: function(stateName) {
return $.ajax({
type: 'POST',
url: getStatesByName + '?stateName=' + stateName,
async: false,
});
}