bluegreymouse
bluegreymouse

Reputation: 137

async forEach loops making api calls

I have a function that makes an api call and a second function that loops through the data of the first function and makes an api call each iteration. I'm trying to use the async library to make this happen but the 2nd function is still running asynchronously instead of waiting to finish. So I end up running function 1 runs, function 2 starts, but final callback runs before function 2 finishes.

async.series([
        function (callback) {
          //api call
          getShelves.execute(function (err, shelves) {
            if (err) { return callback(err); }
            async.forEach(shelves.items, function (shelf, callback) {
              var shelfObj = {id: shelf.id, title: shelf.title, books: []};
              bookShelves.push(shelfObj);
              callback();
            });

            //sort numerically to make placing books easier
            bookShelves.sort(function (a, b) {return a.id - b.id; });
            callback();
          });
        },
        function (callback) {
          async.forEach(bookShelves, function (shelf, callback) {
            //api call
            getBooks.execute(function (err, books) {
              if (err) { return callback(err); }
              if (books.items) {
                async.forEach(books.items, function (book, callback) {
                  var bookObj = {title: book.volumeInfo.title};
                  bookShelves[shelf.id].books.push(bookObj);
                  callback();
                });
              }
              callback();
            });
          });
          callback();
        }
      ], function (err) {
        if (err) { console.log('error'); }
        res.render('collection', { shelves: bookShelves });
      });
    });

EDIT: Working now thanks guys

function (callback) {
          async.forEach(bookShelves, function (shelf, callback) {
            getBooks.execute(function (err, books) {
              if (err) { return callback(err); }
              if (books.items) {
                async.forEach(books.items, function (book, callback) {
                  var bookObj = {title: book.volumeInfo.title};
                  bookShelves[shelf.id].books.push(bookObj);
                  console.log(book.volumeInfo.title);

                  //callback to continue book loop
                  callback();
                }, function () {
                  //callback to continue shelf loop
                  callback();
                });
              }else{
                callback();
               }
            });
          }, function () {
            //callback to end function and move to next. However this is never reached
            callback();
          });
        }

Upvotes: 0

Views: 2086

Answers (2)

Peter Lyons
Peter Lyons

Reputation: 146064

function loadShelf(shelf, callback) {
  //_.pick is handy for this FYI
  var shelfObj = {id: shelf.id, title: shelf.title};
  //presumably your getBooks call takes a shelf id to relate the
  //getBooks is an asynchronous DB or API call presumably
  getBooks(shelf.id, function (error, books) {
    if (error) {
      callback(error);
      return;
    }
    //This is an in-memory array. No async needed.
    shelfObj.books = books.map(function (book) {
      return {title: book.volumeInfo.title};
    });
    callback(null, shelfObj);
  });
}

getShelves.execute(function (error, dbShelves) {
  if (error) {
    res.render('error', error); //pseudo-code error handling
    return;
  }
  async.each(dbShelves, loadShelf, function (error, fullShelves) {
    if (error) {
      res.render('error', error); //pseudo-code error handling
      return;
    }
    //sort numerically to make placing books easier
    var sortedShelves = fullShelves.sort(function (a, b) {return a.id - b.id; });
    res.render('collection', { shelves: sortedShelves });
});

Upvotes: 0

Leo Nyx
Leo Nyx

Reputation: 696

The second function in your series calls its callback immidiately, not waiting until async.forEach iteration finishes. Instead, try this to call it afterwards:

    function (callback) {
      async.forEach(bookShelves, function (shelf, callback) {
        //api call
        //... skipped ...
      }, function() {
          callback();
      });
    }

Upvotes: 1

Related Questions