So I have done quite a bit of troubleshooting on this and am banging my head against a wall. For the most part, I'm pretty familiar with promises and how they work, and have used them on a few projects. I'm having a little trouble with getting all my promises to finish in making several calls for different calendar data from the Google Calendar API and for the script to calculate the length of the resulting array to use in callback functions. Here is the relevant code:
(function($){
var baseUrl = 'https://www.googleapis.com/calendar/v3/calendars/',
apiKey = 'xxxxxxxxxxxx',
calendarData = [],
events = [],
allCalendars = [],
eventsToDisplay = 9;
/* Get all the calendars that we have registered */
function getCalendars() {
return $.getJSON(ajaxurl, {action: 'wps_get_calendars'});
}
/* Get the events for a calendar by the calendar ID */
function getCalendarEvents(calendarID) {
return $.getJSON(baseUrl + calendarID + '/events', {
maxResults: '4',
orderBy: 'startTime',
timeMin: moment().format(),
singleEvents: true,
key: apiKey
}).success(function(data){
calendarData.push(data.items);
});
/* Create a collection of promises */
var promises = getCalendars().then(function(calendars){
var deferreds = [];
calendars.forEach(function(calendar){
deferreds.push(
getCalendarEvents(calendar.googleCalendarId)
);
});
return deferreds;
});
/* Wait until all the promises have completed, then sort the events */
$.when.apply($, promises).then(concatEvents);
})(jQuery);
Basically the issue is at the very last call to $.when
as I am waiting for the array of promises I have to complete. $.when
doesn't seem to be working since if I try to log the calendarData
array to the console in the $.when
callback, it returns an array that doesn't have a calculated length. The only way I've been able to figure out how to do this is to use setTimeout
in the callback, and set it to around 2000 milliseconds, but this isn't ideal since depending on network connectivity, API availability, etc., the time to receive all the data could be completely different.
Just as an idea of what I'm seeing in the console, I get this "length-less" array when I try to log the result in the $.when
callback, which can't be iterated through because the script seems to think it's empty:
Any idea what I'm doing wrong here? Thanks in advance for any help.
You had several structural issues in how your code was working. Issues I identified:
.success()
. Use .then()
.$.when()
there to return a single promise that resolves to an array of results.$.when()
is pretty wonky in how it returns results. It returns them as separate arguments, not as an array of results (it does not work like the ES6 standard Promise.all()
does).$.when()
if you use jQuery ajax promises directly with $.when()
. getCalendar()
so it returns a single promise that resolves to an array of calendars, thus making it much simpler to use.This should get you the results you want and it gets rid of some of the higher scoped variable and side effects you were relying on.
(function($){
var baseUrl = 'https://www.googleapis.com/calendar/v3/calendars/',
apiKey = 'xxxxxxxxxxxx',
events = [],
eventsToDisplay = 9;
/* Get all the calendars that we have registered */
function getCalendars() {
return $.getJSON(ajaxurl, {action: 'wps_get_calendars'}).then(function(calendars){
var promises = calendars.map(function(calendar) {
return getCalendarEvents(calendar.googleCalendarId);
});
return $.when.apply($, promises).then(function() {
// convert arguments to a single array as our resolved value
return [].slice.call(arguments);
});
});
}
/* Get the events for a calendar by the calendar ID */
function getCalendarEvents(calendarID) {
return $.getJSON(baseUrl + calendarID + '/events', {
maxResults: '4',
orderBy: 'startTime',
timeMin: moment().format(),
singleEvents: true,
key: apiKey
}).then(function(data){
// make resolved value be just data.items
return data.items;
});
/* get all calendars */
getCalendars().then(function(calendars){
// process array of calendars here
console.log(calendars);
});
})(jQuery);
In addition, it is very easy to get confused by how $.when()
works. Here's some general info to explain it.
$.when()
does not resolve to an array of results. Instead, it passes each result as a separate argument to the callback. So, if you have three results, then it does this:
$.when(p1, p2, p3).then(function(r1, r2, r3) {
console.log(r1, r2, r3);
});
Then, on top of that a jQuery Ajax call doesn't resolve to a single value either, it resolves to three values. So, when you pass N jQuery ajax promises to $.when()
you get, N arguments to your callback where each argument is an array of three values (the three values from a jQuery Ajax promise).
So, if you want to process an arbitrary number of promise results in $.when()
, you have to use the arguments
object passed to your callback and iterate that.
function processWhenResults() {
for (var i = 0; i < arguments.length; i++) {
// output each ajax result
// arguments[i] is an array of results for each corresponding ajax call
// arguments[i][0] is the actual Ajax result value
console.log(arguments[i][0]);
}
}
$.when.apply($, promises).then(processWhenResults);
Or, you can do as I did in my suggested code above and convert the arguments object to an array of results so you can use normal array functions on it.
In addition, .success()
has been deprecated. You should not use it. Use .then()
instead.
Haven't played with them for a bit but you should be able to take this and run with it.
/* Create a collection of promises */
var control = $.Deferred();
var deferreds = [];
getcalendars().done(function(calendars){
calendars.forEach(function(calendar){
deferreds.push(
getCalendarEvents(calendar.googleCalendarId)
);
});
control.resolve();
});
control.done(function(){
/* Wait until all the promises have completed, then sort the events */
$.when.apply($, deferreds).done(function() {concatEvents}); })
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.