1

I am using closure inside for loop to save categories to categories table after article is saved.

article.save(function (err, newArticle) {
    if (err) throw err;
    console.log('article created ', newArticle._id);

    for (var i = 0; i < categories.length; i++) {

        (function (index) {
            var category_article = new category_article_model({
                "category": categories[index],
                "article_id": newArticle._id
            });

            category_article.save(function (err, new_category_article) {
                if (err) throw err;
            })
        }(i));
    }

    return res.status(res.statusCode).send(newArticle);
})

How do I convert the above to use promises?

kittu
  • 6,662
  • 21
  • 91
  • 185
  • 1
    Check out the Bluebird promise library. It has a lot of useful methods for use with promises. I think your issue could be solved with: http://bluebirdjs.com/docs/api/promise.map.html. I can't write it out because I'm at work, so maybe try playing around with it. I have found the library incredibly useful, and it's likely worth learning if you're going to be working with promises at all. – PunDefeated May 18 '18 at 22:27

1 Answers1

2

It seem's you're using MongoDB, which supports promises. We can then use async/await (node >= 7.6), to make the code cleaner, and use Promise.all in order to wait until all categories are saved.

app.post('/some/article', async(req, res) => {
                        // ^^^ notice async keyword
    const newArticle = await article.save();
    console.log('article created ', newArticle._id);

    // This will return an array of promises
    const categoryPromises = categories.map(category => {
        return new category_article_model({
            "category": category,
            "article_id": newArticle._id
        }).save(); // return a promise
    })

    // Promise.all takes an array of promises and waits
    // Until all promises are fulfilled
    await Promise.all(categoryPromises);

    // All categories are saved

    res.status(res.statusCode).send(newArticle);
});

As a side note, you should stop using var and start using let/const, doing so, you can remove the closure on your code, which isn't needed anyway.

const categories = [1,2,3,4];
for (let i = 0; i < categories.length; i++) {
   // No need for closures
   setTimeout(() => console.log(`Using let: ${categories[i]}`))
}


for (var j = 0; j < categories.length; j++) {
   // without closure it doesn't work
   setTimeout(() => console.log(`Using var: ${categories[j]}`))
}

Check the following question: What's the difference between using "let" and "var" to declare a variable in JavaScript?

Marcos Casagrande
  • 37,983
  • 8
  • 84
  • 98