jQuery 中的多个嵌套 AJAX 调用:如何正确执行而不回退到同步?
Multiple nested AJAX calls in jQuery: How to do it right, without falling back to synchronous?
我知道,这里已经有很多关于 AJAX 和同步性的问题,但不知何故我没有找到正确的答案。请温柔一点,我是菜鸟,所以,这可能是重复的。尽管如此,即使是暗示这一点也可能会帮助我和其他人找到合适的答案。
函数
$.fn.facebookEvents = function(options){
var fbEvents = 'https://graph.facebook.com/'+options.id+'/events/?access_token='+options.access_token+'&since=now&limit=500';
var events = [];
$.when($.getJSON(fbEvents)).then(function(json){
$.each(json.data, function(){
$.getJSON('https://graph.facebook.com/'+this.id+'/?access_token='+options.access_token, function(jsonData){
events.push(jsonData);
console.log(events); // here array "events" is filled successively
});
console.log(events); // here array "events" remains empty
});
}).done(function(){
$("#fb_data_live").html(
$("#fb_events").render(events)
);
});
};
此函数中发生的事情是对 Facebook 页面事件列表的 AJAX 调用(通过 jQuerys getJSON shorthand)。这将 return 代表每个日期的 JSON 个对象的列表。一个对象作为响应的示例:
{
"end_time": "2017-03-16T23:00:00+0100",
"location": "Yuca K\u00f6ln",
"name": "K\u00f6rner // G\u00e4nsehaut Tour 2017 // K\u00f6ln",
"start_time": "2017-03-16T20:00:00+0100",
"timezone": "Europe/Berlin",
"id": "985939951529300"
},
不幸的是,这些缺少我们需要的细节。它们是隐藏的(嵌套的)并且可以在进行第二个 getJSON(函数中的第 8 行)之后使用每个对象的个体 "id" 找到。现在 Facebook 回复:
{
"description": "http://www.eventim.de/koerner\nTickets ab sofort exklusiv auf eventim.de und ab MI 07.09. \u00fcberall wo es Tickets gibt sowie auf contrapromotion.com.",
"end_time": "2017-03-16T23:00:00+0100",
"is_date_only": false,
"location": "Yuca K\u00f6ln",
"name": "K\u00f6rner // G\u00e4nsehaut Tour 2017 // K\u00f6ln",
"owner": {
"name": "K\u00f6rner",
"category": "Musician/Band",
"id": "366592010215263"
},
"privacy": "OPEN",
"start_time": "2017-03-16T20:00:00+0100",
"timezone": "Europe/Berlin",
"updated_time": "2016-09-28T10:31:47+0000",
"venue": {
"name": "Yuca K\u00f6ln"
},
"id": "985939951529300"
}
Et voila,我正在寻找的细节。在进行第二次(嵌套)AJAX 调用后,我将 JSON 数据推送到数组 "events" 中(第 12 行)。
问题
这应该会在每次迭代时填满数组。不过,看看这两个'console.logs'。第一个 return 是填充的数组,第二个 return 是一个空数组...如果 AJAX 调用是同步进行的(这不是应该的方式), 例如通过在函数之前添加 $.ajaxSetup({async: false});
,它可以正常工作。数组已填充并可以渲染(第 18 行)。
问题
如何解释此行为以及如何正确完成此功能?我知道,一定有办法使用 deferred 和 promises 吗?我以为我做到了,通过使用 .when.then.done... 显然我错了。
谨致问候,
朱利叶斯
显然你对承诺一无所知。我想我会以其他方式完成它,但是使用承诺,你可以执行以下操作:
$.fn.facebookEvents = function(options){
var fbEvents = 'https://graph.facebook.com/'+options.id+'/events/?access_token='+options.access_token+'&since=now&limit=500';
var events = [];
var deferred=$.Deferred();
var promises=[];
var done=false;
$.when(deferred).then(function(events) {
console.log(events);
});
$.getJSON(fbEvents).done(function(json){
$.each(json.data, function(){
promises.push($.getJSON('https://graph.facebook.com/'+this.id+'/?access_token='+options.access_token).done(function(jsonData){
events.push(jsonData);
var completed=promises.filter(function(element) { return element.state=='pending' }).length==0;
if(done&&completed) {
deferred.resolve(events);
}
}));
});
done=true;
});
};
感谢您的回答。为了完整起见,我想出了一个替代解决方案。
$.fn.facebookEvents = function(options){
var fbEvents = 'https://graph.facebook.com/'+options.id+'/events/?access_token='+options.access_token+'&since=now&limit=500';
var events = [];
var eventsData = [];
var i;
// first call
function A() {
return $.getJSON(fbEvents).then(function(json){
events.push(json);
i = events[0].data.length;
});
};
// second call for objects details
function B(resultFromA) {
$.each(events[0].data, function(){
$.getJSON('https://graph.facebook.com/'+this.id+'/?access_token='+options.access_token, function(jsonData){
eventsData.push(jsonData);
i--;
if ( i == 0 ){
output(eventsData);
};
});
});
};
A().then(B);
function output() {
//render here
};
};
我知道,这里已经有很多关于 AJAX 和同步性的问题,但不知何故我没有找到正确的答案。请温柔一点,我是菜鸟,所以,这可能是重复的。尽管如此,即使是暗示这一点也可能会帮助我和其他人找到合适的答案。
函数
$.fn.facebookEvents = function(options){
var fbEvents = 'https://graph.facebook.com/'+options.id+'/events/?access_token='+options.access_token+'&since=now&limit=500';
var events = [];
$.when($.getJSON(fbEvents)).then(function(json){
$.each(json.data, function(){
$.getJSON('https://graph.facebook.com/'+this.id+'/?access_token='+options.access_token, function(jsonData){
events.push(jsonData);
console.log(events); // here array "events" is filled successively
});
console.log(events); // here array "events" remains empty
});
}).done(function(){
$("#fb_data_live").html(
$("#fb_events").render(events)
);
});
};
此函数中发生的事情是对 Facebook 页面事件列表的 AJAX 调用(通过 jQuerys getJSON shorthand)。这将 return 代表每个日期的 JSON 个对象的列表。一个对象作为响应的示例:
{
"end_time": "2017-03-16T23:00:00+0100",
"location": "Yuca K\u00f6ln",
"name": "K\u00f6rner // G\u00e4nsehaut Tour 2017 // K\u00f6ln",
"start_time": "2017-03-16T20:00:00+0100",
"timezone": "Europe/Berlin",
"id": "985939951529300"
},
不幸的是,这些缺少我们需要的细节。它们是隐藏的(嵌套的)并且可以在进行第二个 getJSON(函数中的第 8 行)之后使用每个对象的个体 "id" 找到。现在 Facebook 回复:
{
"description": "http://www.eventim.de/koerner\nTickets ab sofort exklusiv auf eventim.de und ab MI 07.09. \u00fcberall wo es Tickets gibt sowie auf contrapromotion.com.",
"end_time": "2017-03-16T23:00:00+0100",
"is_date_only": false,
"location": "Yuca K\u00f6ln",
"name": "K\u00f6rner // G\u00e4nsehaut Tour 2017 // K\u00f6ln",
"owner": {
"name": "K\u00f6rner",
"category": "Musician/Band",
"id": "366592010215263"
},
"privacy": "OPEN",
"start_time": "2017-03-16T20:00:00+0100",
"timezone": "Europe/Berlin",
"updated_time": "2016-09-28T10:31:47+0000",
"venue": {
"name": "Yuca K\u00f6ln"
},
"id": "985939951529300"
}
Et voila,我正在寻找的细节。在进行第二次(嵌套)AJAX 调用后,我将 JSON 数据推送到数组 "events" 中(第 12 行)。
问题
这应该会在每次迭代时填满数组。不过,看看这两个'console.logs'。第一个 return 是填充的数组,第二个 return 是一个空数组...如果 AJAX 调用是同步进行的(这不是应该的方式), 例如通过在函数之前添加 $.ajaxSetup({async: false});
,它可以正常工作。数组已填充并可以渲染(第 18 行)。
问题
如何解释此行为以及如何正确完成此功能?我知道,一定有办法使用 deferred 和 promises 吗?我以为我做到了,通过使用 .when.then.done... 显然我错了。
谨致问候, 朱利叶斯
显然你对承诺一无所知。我想我会以其他方式完成它,但是使用承诺,你可以执行以下操作:
$.fn.facebookEvents = function(options){
var fbEvents = 'https://graph.facebook.com/'+options.id+'/events/?access_token='+options.access_token+'&since=now&limit=500';
var events = [];
var deferred=$.Deferred();
var promises=[];
var done=false;
$.when(deferred).then(function(events) {
console.log(events);
});
$.getJSON(fbEvents).done(function(json){
$.each(json.data, function(){
promises.push($.getJSON('https://graph.facebook.com/'+this.id+'/?access_token='+options.access_token).done(function(jsonData){
events.push(jsonData);
var completed=promises.filter(function(element) { return element.state=='pending' }).length==0;
if(done&&completed) {
deferred.resolve(events);
}
}));
});
done=true;
});
};
感谢您的回答。为了完整起见,我想出了一个替代解决方案。
$.fn.facebookEvents = function(options){
var fbEvents = 'https://graph.facebook.com/'+options.id+'/events/?access_token='+options.access_token+'&since=now&limit=500';
var events = [];
var eventsData = [];
var i;
// first call
function A() {
return $.getJSON(fbEvents).then(function(json){
events.push(json);
i = events[0].data.length;
});
};
// second call for objects details
function B(resultFromA) {
$.each(events[0].data, function(){
$.getJSON('https://graph.facebook.com/'+this.id+'/?access_token='+options.access_token, function(jsonData){
eventsData.push(jsonData);
i--;
if ( i == 0 ){
output(eventsData);
};
});
});
};
A().then(B);
function output() {
//render here
};
};