Question

I'm trying to get all of a user's events, for all of their calendars on Google Calendar. I'm doing this by first making the call to gapi.client.calendar.calendarList.list, and then using the IDs retrieved in the call to gapi.client.calendar.events.list. However, this is resulting in some very weird results. Here is the code:

getAllEvents: function() {
        var deferred = $q.defer(),
        // get all calendars that the user has on Google Calendar
        getCalendars = function() {
          gapi.client.load('calendar', 'v3', function() {
              var request = gapi.client.calendar.calendarList.list({});
              request.execute(function(resp) {
                  if(!resp.error) {
                    var calendarIds = [];
                    for(var i = 0; i < resp.items.length; i++) {
                      calendarIds.push(resp.items[i].id);
                    }
                    getEvents(calendarIds);
                  }
                  else {
                    deferred.reject(resp.error);
                  }
              });
          });
        },
        // get all events for each calendar that was found
        getEvents = function(calendarIds) {
          var events = [];

          for(var i = 0; i < calendarIds.length; i++) {
            // bind i to function to allow asynchronous functions inside for loop
            (function(cntr) {
              var request = gapi.client.calendar.events.list({
                calendarId: calendarIds[i]
              });

              request.execute(function(resp) {
                  if(!resp.error) {
                    for(var j = 0; j < resp.items.length; j++) {
                      console.log(j);
                      events.push(resp.items[j]);
                    }
                  }
                  else {
                    deferred.reject(resp.error);
                  }
              });
            })(i);
          }
          console.log(events);
          deferred.resolve(events);
        };

        // login to google API before making calls
        gapi.auth.authorize({ 
              client_id: this.clientId, 
              scope: this.scopes, 
              immediate: true, 
        }, getCalendars);

        return deferred.promise;
      }

This retrieves the calendar IDs correctly, and even retrieves all the events correctly. However, I think the way I'm doing the asynchronous calls is causing some problems. If I console.log the events array after the inner for loop, it has 110 items but a length of 0 and none of the items can be accessed via their index. If I console.log something inside the for loop, it prints after the console.log(events). Lastly, if I console.log the value of j within the inner for loop, the values are logged out of order, say 0...19, then 0...86.

Any ideas what I am doing wrong here? Like I said, it's retrieving data correctly but I think something is going wrong with the asynchronous calls. Thanks

Was it helpful?

Solution

As I thought, the main reason for these strange errors was the use of the for loop with the asynchronous calls. In order to avoid the for loop, I decided to use Angular's promises more to my advantage:

  • Get all calendars, and return a promise
  • After call to get all calendars completes, create a promise for each event query needed
  • use Angular's $q.all to make all of the calls using the promises created, THEN return all the events found.

Here's the new code:

getAllEvents: function() {
        var deferred = $q.defer(),

        // get all calendars that the user has on Google Calendar
        getCalendars = function() {
          var calDeferred = $q.defer();

          gapi.client.load('calendar', 'v3', function() {
              var request = gapi.client.calendar.calendarList.list({});
              request.execute(function(resp) {
                  if(!resp.error) {
                    var calendarIds = [];
                    for(var i = 0; i < resp.items.length; i++) {
                      calendarIds.push(resp.items[i].id);
                    }
                    calDeferred.resolve(calendarIds);
                  }
                  else {
                    calDeferred.reject(resp.error);
                  }
              });
          });

          return calDeferred.promise;
        },
        // get all events for a calendar
        getEvents = function(calendarId) {
          var events = [],
              eventsDeferred = $q.defer();

          var request = gapi.client.calendar.events.list({
            calendarId: calendarId
          });

          request.execute(function(resp) {
              if(!resp.error) {
                for(var j = 0; j < resp.items.length; j++) {
                  events.push(resp.items[j]);
                }
                eventsDeferred.resolve(events);
              }
              else {
                eventsDeferred.reject(resp.error);
              }
          });

          return eventsDeferred.promise;
        },
        getAllEvents = function() {
          getCalendars().then(function (calendarIds) {
            var eventCalls = [];

            // get promise for each calendar event query
            for(var i = 0; i < calendarIds.length; i++) {
              eventCalls.push(getEvents(calendarIds[i]));
            }

            // make all calls to get all events
            $q.all(eventCalls).then(function(results) {
              var aggregatedData = [];

              angular.forEach(results, function (result) {
                  aggregatedData = aggregatedData.concat(result);
              });

              deferred.resolve(aggregatedData);
            });
          },
          function (errorMessage) {
            deferred.reject(errorMessage);
          });
        };

        // login to google API before making calls
        gapi.auth.authorize({ 
              client_id: this.clientId, 
              scope: this.scopes, 
              immediate: true, 
        }, getAllEvents);

        return deferred.promise;
      }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top