我使用jQuery。我不希望我的应用程序进行并行的AJAX调用,每个调用必须在开始之前等待上一个调用完成。如何实现?有任何辅助工具吗?
更新 如果有XMLHttpRequest或jQuery.post的同步版本,我想知道。但是,顺序执行不等于同步执行,我需要一种异步且顺序执行的解决方案。
我使用jQuery。我不希望我的应用程序进行并行的AJAX调用,每个调用必须在开始之前等待上一个调用完成。如何实现?有任何辅助工具吗?
更新 如果有XMLHttpRequest或jQuery.post的同步版本,我想知道。但是,顺序执行不等于同步执行,我需要一种异步且顺序执行的解决方案。
有一种比使用同步ajax调用更好的方法。Jquery ajax返回一个deferred,因此您可以使用管道链接来确保每个ajax调用在下一个运行之前完成。这是一个可工作的示例,并且有一个更详细的示例,您可以在jsfiddle上进行演示。
// How to force async functions to execute sequentially
// by using deferred pipe chaining.
// The master deferred.
var dfd = $.Deferred(), // Master deferred
dfdNext = dfd; // Next deferred in the chain
x = 0, // Loop index
values = [],
// Simulates $.ajax, but with predictable behaviour.
// You only need to understand that higher 'value' param
// will finish earlier.
simulateAjax = function (value) {
var dfdAjax = $.Deferred();
setTimeout(
function () {
dfdAjax.resolve(value);
},
1000 - (value * 100)
);
return dfdAjax.promise();
},
// This would be a user function that makes an ajax request.
// In normal code you'd be using $.ajax instead of simulateAjax.
requestAjax = function (value) {
return simulateAjax(value);
};
// Start the pipe chain. You should be able to do
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();
// Deferred pipe chaining.
// What you want to note here is that an new
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {
values.push(x);
dfdNext = dfdNext.pipe(function () {
var value = values.shift();
return requestAjax(value).
done(function(response) {
// Process the response here.
});
});
}
有些人评论说他们不知道这段代码是干什么的。为了理解它,你首先需要了解JavaScript Promise。我非常确定Promise很快将成为JavaScript语言的原生功能,所以这应该会给你一个很好的学习动力。
async: false
设置为请求参数来实现。这样做会引发与您建议的行为完全相同的行为。 - kamelkevasync: false
会阻止代码中等待请求返回时触发的其他事件。这是极不鼓励的,因为即使像点击这样的浏览器事件也不会被触发。我提供的代码允许异步请求仍然以严格的顺序运行。 - Todd Chaffee(async () => {
for(f of ['1.json','2.json','3.json']){
var json = await $.getJSON(f);
console.log(json)
};
})()
更多信息请参见MDN
现代的 jQuery 异步操作排序方式是使用它们已经返回的 promises 和 promises 支持的流程控制,这在之前年份的任何其他答案中都没有展示。
例如,假设你想要使用 $.getScript()
加载多个脚本,但这些脚本必须按顺序加载,以便第二个脚本在第一个脚本完成之前不会被加载/运行,而且你想知道它们何时全部完成。你可以直接使用 $.getScript()
已经返回的 promise。为了简单起见,你可以像这样在一个 for
循环中使用 await
这个 promise:
async function loadScripts(scriptsToLoad) {
for (const src of scriptsToLoad) {
await $.getScript(src);
}
}
loadScripts([url1, url2, url3]).then(() => {
console.log("all done loading scripts");
}).catch(err => {
console.log(err);
});
由于所有jQuery异步操作现在都返回承诺(并且多年来一直如此),因此您可以将此概念扩展到任何与jQuery的Ajax相关的操作。
另外,请注意,其他答案中尝试在新承诺或jQuery延迟中包装jQuery操作的所有尝试已过时,并被认为是承诺反模式,因为当操作本身已经返回一个承诺时,您可以直接使用该承诺,而无需尝试将其包装在自己的新承诺中。
await
的使用,这就是人们应该关注的! - tobybot您可以使用Promise使Ajax调用按顺序进行。使用Array.push和pop Promise方法,顺序的Ajax调用将变得更加容易。
var promises = [Promise.resolve()];
function methodThatReturnsAPromise(id) {
return new Promise((resolve, reject) => {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/'+id,
dataType:'json',
success: function(data)
{
console.log("Ajax Request Id"+id);
console.log(data);
resolve();
}
});
});
}
function pushPromise(id)
{
promises.push(promises.pop().then(function(){
return methodThatReturnsAPromise(id)}));
}
pushPromise(1);
pushPromise(3);
pushPromise(2);
你可以像Nosredna所说的那样链接回调函数来完成这个任务,这是最好的方法。我不建议使用同步XMLHttpRequest,因为它们会锁定整个应用程序。
据我所知,没有太多辅助工具可以做到这一点,但你可以实现类似于回调FIFO的东西。
你可以尝试使用叙述性 JavaScript http://www.neilmix.com/narrativejs/doc/
虽然我自己从未使用过它。如果我想要这样做,我会设置某种抽象来链接异步操作。正如其他人所说,ajax 对象的同步版本会阻塞事件的处理,而它正在等待响应。这会导致浏览器看起来像是冻结了,直到它收到响应。
将async选项设置为false,例如:
$.ajax({ async: false /*, your_other_ajax_options_here */ });
参考资料:Ajax/jQuery.ajax
$.getJSON("http://example.com/jsoncall", function(data) {
process(data);
$.getJSON("http://example.com/jsoncall2", function (data) {
processAgain(data);
$.getJSON("http://example.com/anotherjsoncall", function(data) {
processAgainAndAgain(data);
});
});
});
这样,第二个调用只有在第一个调用的响应已经被接收和处理后才会发出,而第三个调用只有在第二个调用的响应已经被接收和处理后才会发出。这段代码是针对getJSON编写的,但它也可以适用于$.ajax。