如何让一个函数等到所有 jQuery Ajax 请求都在另一个函数中完成?
简而言之,在执行下一个请求之前,我需要等待所有 Ajax 请求完成。但是怎么做?
jQuery 现在为此定义了一个 when function。
它接受任意数量的 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 参数(即您想使用可变数量的参数),它仍然可以完成,但有点棘手。请参阅 Pass in an array of Deferreds to $.when()(也许还有 jQuery .when troubleshooting with variable number of arguments)。
如果您需要更深入地控制 ajax 脚本等的故障模式,您可以保存 .when()
返回的对象 - 它是一个包含所有原始 ajax 查询的 jQuery Promise 对象。您可以对其调用 .then()
或 .fail()
以添加详细的成功/失败处理程序。
如果您想知道文档中所有 ajax
请求何时完成,无论它们存在多少,只需这样使用 $.ajaxStop
事件:
$(document).ajaxStop(function () {
// 0 === $.active
});
在这种情况下,您既不需要猜测应用程序中发生了多少请求,这些请求可能会在未来完成,也不需要深入研究函数的复杂逻辑或查找哪些函数正在执行 HTTP(S) 请求。这里的 $.ajaxStop 也可以绑定到您认为可能会被请求修改的任何 HTML 节点。
更新:
如果您想坚持使用 ES
语法,那么您可以将 Promise.all
用于已知的 ajax
方法:
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
这里有趣的一点是它同时适用于 Promises
和 $.ajax
请求。
这是 jsFiddle 演示。
更新 2:
使用 async/await 语法的更新版本:
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
我找到了我自己的 gnarf 的 good answer,这正是我要找的 :)
jQuery ajax队列
//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);
}
});
注意: 上述答案使用了编写此答案时不存在的功能。我建议使用 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();
});
});
.get()
中引用该函数。这样至少您不会复制该代码。不仅如此,每次声明 function(){}
都会分配内存!如果您可以调用静态定义的函数,这是相当糟糕的做法。
使用 ajaxStop
事件。
例如,假设您在获取 100 个 ajax 请求时有一条 loading ... 消息,并且您希望在加载后隐藏该消息。
从 jQuery doc:
$("#loading").ajaxStop(function() {
$(this).hide();
});
请注意,它将等待该页面上完成的所有 ajax 请求。
一个小解决方法是这样的:
// 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();
});
希望能有用...
javascript 是基于事件的,所以你不应该等待,而是设置钩子/回调
您可能只使用 jquery.ajax 的成功/完成方法
或者您可以使用 .ajaxComplete :
$('.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 请求是如何被调用的,以便更精确......
jQuery 允许您指定是否希望 ajax 请求是异步的。您可以简单地使 ajax 请求同步,然后其余代码在它们返回之前不会执行。
例如:
jQuery.ajax({
async: false,
//code
});
正如提到的其他答案,您可以使用 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 promises 等待,直到您想要等待的这些 ajax 完成。我制作了一个简短易读的示例,向您展示 Promise 如何与 ajax 一起使用。
请看下一个示例。我使用 setTimeout
来阐明示例。
// 注意: // resolve() 用于将 Promise 标记为已解决 // reject() 用于将 Promise 标记为被拒绝 $(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'}, 成功: function(data) { setTimeout(function() { $('#image1').attr("src", window.URL.createObjectURL( data)); resolve("Promise ajax1 已解决"); }, 1000); }, 错误:function() { reject("Promise ajax1 被拒绝"); }, }); }); var ajax2 = new Promise((解决,拒绝)=> { $.ajax({ type: "GET", url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512 .png", xhrFields: { responseType: 'blob' }, 成功: function(data) { setTimeout(function() { $('#image2').attr("src", window.URL.createObjectURL(data)) ;resolve("Promise ajax2 已解决"); }, 1500); }, error: function() { reject("Promise ajax2 denied"); }, }); }); var aj ax3 = new Promise((resolve, reject) => { $.ajax({ type: "GET", url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png", xhrFields: { responseType: 'blob' }, 成功: function(data) { setTimeout(function() { $('#image3').attr("src", window.URL.createObjectURL(data)); resolve("Promise ajax3 已解决"); }, 2000); },错误:函数(){拒绝(“承诺ajax3被拒绝”); }, }); }); Promise.all([ajax1, ajax2, ajax3]).then(values => { console.log("我们一直等到 ajax 结束:" + values); console.log("我的几个 ajax 结束了,让我们做点事情吧! !") }, reason => { console.log("Promises failed: " + reason); }); // 或者,如果您想单独等待它们,请这样做 // ajax1.then(values => { // console.log("Promise 1 resolved: " + values) // }, reason => { // 控制台.log("Promise 1 failed:" + reason) // }); }); }); img { 最大宽度:200px;最大高度:100px; }
如果你需要一些简单的东西;一次完成回调
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
在@BBonifield 回答的基础上,我编写了一个实用函数,以便信号量逻辑不会在所有ajax 调用中传播。
untilAjax
是在所有 ajaxCall 完成时调用回调函数的实用函数。
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.
});
}
如果您从头开始,我强烈建议您使用 $.when()。
即使这个问题有超过一百万个答案,我仍然没有找到任何对我的案例有用的东西。假设您必须处理现有的代码库,已经进行了一些 ajax 调用,并且不想引入承诺的复杂性和/或重做整个事情。
我们可以轻松地利用 jQuery .data
、.on
和 .trigger
函数,这些函数自古以来就是 jQuery 的一部分。
我的解决方案的好处是:
很明显回调究竟取决于什么
函数 triggerNowOrOnLoaded 不关心数据是否已经加载或者我们仍在等待它
将其插入现有代码非常容易
$(function() { // 等待帖子加载 triggerNowOrOnLoaded("posts", function() { var $body = $("body"); var posts = $body.data("posts"); $body .append("
当所有 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;变量最大值= 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(函数(信息){ console.log(info) })
为了扩展亚历克斯的答案,我有一个带有可变参数和承诺的例子。我想通过 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/
我找到了简单的方法,它使用 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
我的解决方案如下
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);
});
工作得很好。我尝试了很多不同的方法来做到这一点,但我发现这是最简单和最可重用的。希望能帮助到你
看看我的解决方案:
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
以下解决方案使用 $when 对我有用
$.when(master.GetStateByName(stateName)).done(function(response) {
if (response) {
}
});
GetStateByName: function(stateName) {
return $.ajax({
type: 'POST',
url: getStatesByName + '?stateName=' + stateName,
async: false,
});
}
这对我有用 这很简单
return $.ajax({
type: 'POST',
url: urlBaseUrl
data: {someData:someData},
dataType: "json",
success: function(resultData) {
}
});
试试这个方法。在 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;
}
setTimeout()
不 take a sleep
。在这种情况下,您只需阻止所有选项卡,直到 done
变为真。
done
永远不会是真的。如果 while 循环正在运行,则事件循环无法继续,因此永远不会运行回调到 ajax 成功。
$.when
返回一个Promise
对象,该对象具有更多有用的方法,而不仅仅是.done
。例如,使用.then(onSuccess, onFailure)
方法,您可以在两个请求都成功或其中至少一个失败时做出反应。fail
的情况。与done
不同,fail
在第一次失败时立即触发并忽略剩余的延迟。onFailure
函数的事实。正如我在对 OP 问题的评论中指出的那样:他可能想更准确地指出他所说的“完成”是什么意思。关于fail
与done
的行为不同这一事实,“Ryan Mohr”也确实有一个很好的观点,我想关于Promises
的一些进一步阅读是html5rocks.com/en/tutorials/es6/promises