Ajax 循环内调用需要顺序响应
Ajax calls inside loop need sequential responses
我需要进行 3 次或更少的 ajax 调用,并且需要按照请求的相同顺序将响应附加到 dom。
我有以下功能,但问题是当我得到的响应附加到 dom.
时,它们的顺序不一定正确
我不想使用 async: false 属性 因为它会阻塞 UI 而且这当然会影响性能。
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
});
}
};
您需要将文章附加到特定位置,例如基于您拥有的 i
变量。或者您可以等待所有请求,然后按顺序附加它们。像这样:
mod.getArticles = function( ){
var load = function( id ) {
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
});
};
var onDone = function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
};
var requests = [];
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
requests.push(load(mod.vars.pushIds[i]));
}
$.when.apply(this, requests).done(function() {
var results = requests.length > 1 ? arguments : [arguments];
for( var i = 0; i < results.length; i++ ){
onDone(results[i][0]);
}
});
};
这是一个示例,使用 i
在它们全部完成加载后以正确的顺序附加它们:
mod.getArticles = function( ){
// initialize an empty array of proper size
var articles = Array(3 - mod.vars.ajaxCount);
var completed = 0;
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
// prevent i from being 3 inside of done callback
(function (i){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
completed++;
if (data.length) {
// store to array in proper index
articles[i - mod.vars.ajaxCount] = data;
} else {
console.error('get article ajax output error');
}
// if all are completed, push in proper order
if (completed == 3 - mod.vars.ajaxCount) {
// iterate through articles
for (var j = mod.vars.ajaxCount; j < 3; j++) {
// check if article loaded properly
if (articles[j - mod.vars.ajaxCount]) {
mod.appendArticle(articles[j - mod.vars.ajaxCount]);
}
}
}
});
}(i));
}
};
var success1 = $.ajax...
var success2 = $.ajax...
var success3 = $.ajax...
$.when(success1, success2, success3).apply(ans1, ans2, ans3) {
finalDOM = ans1[0]+ans2[0]+ans3[0];
}
检查 this 以获得更多参考。这仍然是异步的,但它等待所有这些完成。您已经知道调用顺序,因为它是通过您的代码完成的,所以相应地添加 dom 元素。
尝试利用 mod.vars
数组中的项目作为索引;要设置为 $.ajaxSettings
的 id
属性,请在响应数组中的 this.id
索引处设置返回的 data
。当所有请求完成时,results
array
应与 mod.vars
值的顺序相同。
var mod = {
"vars": [0, 1, 2]
};
mod.getArticles = function () {
var results = [];
var ids = this.vars;
var request = function request(id) {
return $.ajax({
type: "GET",
url: "/ajax/article/" + id + "/",
// set `id` at `$.ajaxSettings` ,
// available at returned `jqxhr` object
id: id
})
.then(function (data, textStatus, jqxhr) {
// insert response `data` at `id` index within `results` array
console.log(data); // `data` returned unordered
// set `data` at `id` index within `results
results[this.id] = data;
return results[this.id]
}, function (jqxhr, textStatus, errorThrown) {
console.log("get article ajax error", errorThrown);
return jqxhr
});
};
return $.when.apply(this, $.map(ids, function (id) {
return request(id)
}))
.then(function () {
$.map(arguments, function (value, key) {
if (value.length) {
// append `value`:`data` returned by `$.ajax()`,
// in order set by `mod.vars` items:`id` item at `request`
mod.appendArticle(value);
} else {
console.error("get article ajax output error");
};
})
});
};
mod.getArticles();
jsfiddle http://jsfiddle.net/6j7vempp/2/
完全依赖于闭包的解决方案在一定程度上是可行的。他们将始终如一地以正确的顺序附加单个 mod.getArticles()
调用的文章。但是在第一个完全满意之前考虑第二次调用。由于进程的异步性,第二次调用的文章集可以想象地附加在第一次调用之前。
一个更好的解决方案将保证即使是 mod.getArticles()
调用的快速触发序列也会:
- 以正确的顺序附加每个调用的文章
- 以正确的顺序附加所有文章集
一种方法是,对于每篇文章:
- 将容器 (a div) 同步附加到 DOM 并保留对它的引用
- 内容到达时用异步方式填充容器。
为此,您需要修改 mod.appendArticle()
以接受第二个参数 - 对容器元素的引用。
mod.appendArticle = function(data, $container) {
...
};
为方便起见,您还可以选择创建一个新方法,mod.appendArticleContainer()
,它创建一个 div,将其附加到 DOM 和 returns 一个引用
mod.appendArticleContainer = function() {
//put a container somewhere in the DOM, and return a reference to it.
return $("<div/>").appendTo("wherever");
};
现在,mod.getArticles()
还是很简单的:
mod.getArticles = function() {
//Here, .slice() returns a new array containing the required portion of `mod.vars.pushIds`.
//This allows `$.map()` to be used instead of a more cumbersome `for` loop.
var promises = $.map(mod.vars.pushIds.slice(mod.vars.ajaxCount, 3), function(id) {
var $container = mod.appendArticleContainer();//<<< synchronous creation of a container
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML'
}).then(function(data) {
if (data.length) {
mod.appendArticle(data, $container);//<<< asynchronous insertion of content
} else {
return $.Deferred().reject(new Error("get article ajax output error"));
}
}).then(null, function(e) {
$container.remove();//container will never be filled, so can be removed.
console.error(e);
return $.when(); // mark promise as "handled"
});
});
return $.when.apply(null, promises);
};
mod.getArticles()
现在 returns 向其调用者承诺完成,必要时允许进一步链接。
而不是使用 for 循环。在上一个函数的响应部分调用您的函数。
//create a global variable
var counter = 0;
function yourFunc(){
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[counter ];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
//increment & check your loop condition here, so that your responses will be appended in same order
counter++;
if (counter < 3)
{ yourFunc(); }
});
};
}
我遇到了同样的问题,我使用以下方法解决了这个问题。
只需使用 async 获取序列响应
<script type="text/javascript">
var ajax1 = $.ajax({
async: false,
url: 'url',
type: 'POST',
data: {'Data'},
})
.done(function(response) {
console.log(response);
});
我需要进行 3 次或更少的 ajax 调用,并且需要按照请求的相同顺序将响应附加到 dom。
我有以下功能,但问题是当我得到的响应附加到 dom.
时,它们的顺序不一定正确我不想使用 async: false 属性 因为它会阻塞 UI 而且这当然会影响性能。
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
});
}
};
您需要将文章附加到特定位置,例如基于您拥有的 i
变量。或者您可以等待所有请求,然后按顺序附加它们。像这样:
mod.getArticles = function( ){
var load = function( id ) {
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
});
};
var onDone = function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
};
var requests = [];
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
requests.push(load(mod.vars.pushIds[i]));
}
$.when.apply(this, requests).done(function() {
var results = requests.length > 1 ? arguments : [arguments];
for( var i = 0; i < results.length; i++ ){
onDone(results[i][0]);
}
});
};
这是一个示例,使用 i
在它们全部完成加载后以正确的顺序附加它们:
mod.getArticles = function( ){
// initialize an empty array of proper size
var articles = Array(3 - mod.vars.ajaxCount);
var completed = 0;
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
// prevent i from being 3 inside of done callback
(function (i){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
completed++;
if (data.length) {
// store to array in proper index
articles[i - mod.vars.ajaxCount] = data;
} else {
console.error('get article ajax output error');
}
// if all are completed, push in proper order
if (completed == 3 - mod.vars.ajaxCount) {
// iterate through articles
for (var j = mod.vars.ajaxCount; j < 3; j++) {
// check if article loaded properly
if (articles[j - mod.vars.ajaxCount]) {
mod.appendArticle(articles[j - mod.vars.ajaxCount]);
}
}
}
});
}(i));
}
};
var success1 = $.ajax...
var success2 = $.ajax...
var success3 = $.ajax...
$.when(success1, success2, success3).apply(ans1, ans2, ans3) {
finalDOM = ans1[0]+ans2[0]+ans3[0];
}
检查 this 以获得更多参考。这仍然是异步的,但它等待所有这些完成。您已经知道调用顺序,因为它是通过您的代码完成的,所以相应地添加 dom 元素。
尝试利用 mod.vars
数组中的项目作为索引;要设置为 $.ajaxSettings
的 id
属性,请在响应数组中的 this.id
索引处设置返回的 data
。当所有请求完成时,results
array
应与 mod.vars
值的顺序相同。
var mod = {
"vars": [0, 1, 2]
};
mod.getArticles = function () {
var results = [];
var ids = this.vars;
var request = function request(id) {
return $.ajax({
type: "GET",
url: "/ajax/article/" + id + "/",
// set `id` at `$.ajaxSettings` ,
// available at returned `jqxhr` object
id: id
})
.then(function (data, textStatus, jqxhr) {
// insert response `data` at `id` index within `results` array
console.log(data); // `data` returned unordered
// set `data` at `id` index within `results
results[this.id] = data;
return results[this.id]
}, function (jqxhr, textStatus, errorThrown) {
console.log("get article ajax error", errorThrown);
return jqxhr
});
};
return $.when.apply(this, $.map(ids, function (id) {
return request(id)
}))
.then(function () {
$.map(arguments, function (value, key) {
if (value.length) {
// append `value`:`data` returned by `$.ajax()`,
// in order set by `mod.vars` items:`id` item at `request`
mod.appendArticle(value);
} else {
console.error("get article ajax output error");
};
})
});
};
mod.getArticles();
jsfiddle http://jsfiddle.net/6j7vempp/2/
完全依赖于闭包的解决方案在一定程度上是可行的。他们将始终如一地以正确的顺序附加单个 mod.getArticles()
调用的文章。但是在第一个完全满意之前考虑第二次调用。由于进程的异步性,第二次调用的文章集可以想象地附加在第一次调用之前。
一个更好的解决方案将保证即使是 mod.getArticles()
调用的快速触发序列也会:
- 以正确的顺序附加每个调用的文章
- 以正确的顺序附加所有文章集
一种方法是,对于每篇文章:
- 将容器 (a div) 同步附加到 DOM 并保留对它的引用
- 内容到达时用异步方式填充容器。
为此,您需要修改 mod.appendArticle()
以接受第二个参数 - 对容器元素的引用。
mod.appendArticle = function(data, $container) {
...
};
为方便起见,您还可以选择创建一个新方法,mod.appendArticleContainer()
,它创建一个 div,将其附加到 DOM 和 returns 一个引用
mod.appendArticleContainer = function() {
//put a container somewhere in the DOM, and return a reference to it.
return $("<div/>").appendTo("wherever");
};
现在,mod.getArticles()
还是很简单的:
mod.getArticles = function() {
//Here, .slice() returns a new array containing the required portion of `mod.vars.pushIds`.
//This allows `$.map()` to be used instead of a more cumbersome `for` loop.
var promises = $.map(mod.vars.pushIds.slice(mod.vars.ajaxCount, 3), function(id) {
var $container = mod.appendArticleContainer();//<<< synchronous creation of a container
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML'
}).then(function(data) {
if (data.length) {
mod.appendArticle(data, $container);//<<< asynchronous insertion of content
} else {
return $.Deferred().reject(new Error("get article ajax output error"));
}
}).then(null, function(e) {
$container.remove();//container will never be filled, so can be removed.
console.error(e);
return $.when(); // mark promise as "handled"
});
});
return $.when.apply(null, promises);
};
mod.getArticles()
现在 returns 向其调用者承诺完成,必要时允许进一步链接。
而不是使用 for 循环。在上一个函数的响应部分调用您的函数。
//create a global variable
var counter = 0;
function yourFunc(){
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[counter ];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
//increment & check your loop condition here, so that your responses will be appended in same order
counter++;
if (counter < 3)
{ yourFunc(); }
});
};
}
我遇到了同样的问题,我使用以下方法解决了这个问题。 只需使用 async 获取序列响应
<script type="text/javascript">
var ajax1 = $.ajax({
async: false,
url: 'url',
type: 'POST',
data: {'Data'},
})
.done(function(response) {
console.log(response);
});