我该如何让一个函数在另一个函数中的所有jQuery Ajax请求都完成后再执行?
简而言之,我需要在执行下一个请求之前等待所有Ajax请求完成。但是如何实现呢?
我该如何让一个函数在另一个函数中的所有jQuery Ajax请求都完成后再执行?
简而言之,我需要在执行下一个请求之前等待所有Ajax请求完成。但是如何实现呢?
现在 jQuery 定义了一个 when 函数 用于此目的。
它接受任意数量的 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,这可能会在页面开发过程中产生不良影响。.when()
返回的对象 - 它是一个包含所有原始ajax查询的jQuery Promise对象。您可以调用.then()
或.fail()
对其进行详细的成功/失败处理程序。$.when
返回一个Promise
对象,该对象具有更多有用的方法,不仅仅是.done
。例如,使用.then(onSuccess, onFailure)
方法,你可以在两个请求都成功或至少其中一个失败时做出反应。 - skaleefail
情况。与done
不同,fail
在第一个失败时立即触发,并忽略剩余的deferreds。 - Ryan Mohr$.ajaxStop
事件,方法如下:
$(document).ajaxStop(function () {
// 0 === $.active
$(this).unbind('ajaxStop'); // to stop this event repeating further
});
$.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 的好回答,恰好满足了我的需求 :)
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);
}
});
注意:上面的答案使用了在回答这个问题时还不存在的功能。我建议使用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(){}
都会分配内存!如果你可以调用静态定义的函数,那么这种做法非常糟糕。 - Alexis Wilke使用ajaxStop
事件。
例如,假设您在获取100个ajax请求时有一个正在加载...消息,并且您想要在加载完成后隐藏该消息。
来自jQuery 文档:
$("#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();
});
希望这能有所帮助...
jQuery允许您指定您想要的ajax请求是同步还是异步的。您可以简单地使ajax请求同步,然后在它们返回之前,其余代码将不会执行。
例如:
jQuery.ajax({
async: false,
//code
});
$('.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...
}
});
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 请求被完成。我制作了一个简短、易懂的示例来展示 promises 如何与 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>
在@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.
});
}