Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for promise in a forEach loop

I am receiving a list of books from a database as a Promise.

If the initial list of books is successfully loaded, the books are further processed by passing them to a utility function.

Inside the utility function is a forEach loop which loops over each initial book, makes an async call to get additional information, creates a new book object and adds it to an array of new books (called updatedBooks).

Problem: I don't know how to wait until the forEach loop is finished with every book and how to return the array of new books.

Currently, I only get one updated book instead of all

My ultimate goal is to have the list of updated books in the res.json() object

This is my current structure

controller.find(req.query)
    .then(function (entities) {
        awsUtil.addInfo(entities)
            .then(function (updatedBooks) {
                res.json({
                    confirmation: "success",
                    result: updatedBooks
                })
            })
            .catch(function (err) {
                res.json({
                    confirmation: "fail",
                    message: err
                })
            })
    })
    .catch(function (err) {
        res.json({
            confirmation: "fail",
            message: err
        })
    })

Function to get the initial books from MongoDB

find: function (params) {
    return new Promise(function (resolve, reject) {
        Book.find(params, function (err, books) {
            if (err) {
                reject(err)
                return
            }

            console.log("Org. Books List: ", books)
            resolve(books);
        })
    })
}

Utility function to get additional information and return a new array of books

addInfo: function (books) {
    return new Promise(function (resolve, reject) {
        let updatedBooks = [];

        books.forEach(function (book) {
            client.itemLookup({ //Call Amazon API to get book info
                idType: 'ISBN',
                itemId: book.isbn,
                responseGroup: 'ItemAttributes,Images'
            })
                .then(function (results) {
                    const updatedBook = {
                        // The additional info from result gets added here
                    }

                    updatedBooks.push(updatedBook);
                }).catch(function (err) {
                console.log(err);
                reject(err)
            });
        })
        resolve(updatedBooks) //Return the array of new books
    })
}
like image 494
JesterWest Avatar asked Sep 03 '17 19:09

JesterWest


People also ask

Can you use promise inside for/foreach loop?

A similar approach can be applied to use promise inside for loop or while in JavaScript or Node.js. Wrapping It Up In this tutorial, you learned how to handle promise inside for/ forEach loop.

How to create a promise loop using NPM?

Promise Inside For/ForEach Loop Create a folder called promise_loop. Initialize the project folder using npm. mkdir promise_loop cd promise_loop npm init Create a file called helper.js which will make use of the request module to make the API calls. Install the request module using npm. # install request module npm install --save request

How to batch execute a promise in JavaScript?

Batch execution or a promise chain is possible with the Promise.all () method. It’s a handy little function which allows you to pass an array of Promise objects to have them executed and give you a common .then () and .catch () handler. All you have to do is to collect your calls into an array. How? With the array.map () method.

Is it possible to synchronize promise with a recursive function?

Unfortunately, Promise is fully asynchronous and can’t be made to act synchronously. Except, of course, if we don’t mind some hacking! The solution is to create a recursive function. Here’s the full code: Our delay () function hasn’t changed, and neither has our delays array.


1 Answers

Modify the addInfo method so it stores the promises in an array, and return Promise.all instead, which will resolve when all the promises have resolved.

Assuming your methods returns a promise, and it looks like they do, something like

addInfo: function(books) {
  let promises = books.map(function(book) {
    return client.itemLookup({
      idType        : 'ISBN',
      itemId        : book.isbn,
      responseGroup : 'ItemAttributes,Images'
    }).then(function(results) {
      return {
        // The additional info from result gets added here
      }
    });
  })
  return Promise.all(promises); // catch errors where you call "addInfo"
}
like image 130
adeneo Avatar answered Oct 18 '22 22:10

adeneo