Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callback after for-loop finishes in node.js

I need some help with the asynchronous nature of node.js. I have a for loop, which collects data from the database. "result" is an array, which should then be returned to the main function.

user_collection.findOne({
            _id : uid
        }, function(error, user) {
            if(error)
                callback(error)
            else {
                for(var j = 0; j < user.contacts.length; j++) {
                    if(user.contacts[j].accepted == 'true') {
                        user_collection.findOne({
                            _id : user.contacts[j].contactId
                        }, function(error, user) {
                            result.push(user);
                        })
                    } 
                }
                callback(null, result);  // This callback executes before the for-loop ends, ofc
            }
        });

How can I ensure that the callback executes after the loop finished?

like image 770
johnny Avatar asked May 04 '12 11:05

johnny


People also ask

How do you wait for callback function to finish?

JavaScript provides a setTimeout() method which can work with the callback function and the await keyword to wait for a function to finish. The objective of employing these methods is to execute a piece of code after waiting for a specific time.

How do you end a loop in node JS?

TL;DR: use break to exit a loop in JavaScript.

Which is faster promise or callback?

So from my findings i assure you ES6 promises are faster and recommended than old callbacks. I recommend to get a common understanding of JS event loop.

How does callback work in node JS?

js Callback Concept. A callback is a function which is called when a task is completed, thus helps in preventing any kind of blocking and a callback function allows other code to run in the meantime. Callback is called when task get completed and is asynchronous equivalent for a function.


2 Answers

You might want to consider using helper library like async https://github.com/caolan/async

It helps keep code more consistent..

In your case, you can look at the forEach() method

forEach(arr, iterator, callback)

The iterator is called with an item from the list and a callback for when it has finished.

Checkout the unit tests for examples

https://github.com/caolan/async/blob/master/mocha_test/each.js

like image 185
250R Avatar answered Sep 21 '22 15:09

250R


Using ES6 Promises (a promise library can be used for older browsers):

Process all requests guaranteeing synchronous execution (e.g. 1 then 2 then 3)

function asyncFunction (item, cb) {
  setTimeout(() => {
    console.log('done with', item);
    cb();
  }, 100);
}

let requests = [1, 2, 3].reduce((promiseChain, item) => {
    return promiseChain.then(new Promise((resolve) => {
      asyncFunction(item, resolve);
    }));
}, Promise.resolve());

requests.then(() => console.log('done'))

Process all async requests without "synchronous" execution (2 may finish faster than 1)

let requests = [1,2,3].map((item) => {
    return new Promise((resolve) => {
      asyncFunction(item, resolve);
    });
})

Promise.all(requests).then(() => console.log('done'));

I did it on that way

Promise.all(body.schedules.map(function(scheduleId) {
        return new Promise(function(resolve, reject) {
            return scheduleSchema.findOneAndRemove({
                    _id: scheduleId
                })
                .then(function() {
                    logSchema.insert({
                        userId: req.user.id,
                        actId: constants.LOG_SCHEDULE_DELETE.id,
                        extData: scheduleId
                    });
                    resolve();
                })
                .catch(function(err) {
                    reject(err);
                });
        });
    })).then(function() {
        return res.json({
            code: constants.SUCCESS_CODE
        });
    }).catch(function(err) {
        return res.json(constants.DATABASE_ERROR);
    });

The last example

function callback (result) { console.log('all done'); }

[1, 2, 3].forEach((item, index, array) => {
  asyncFunction(item, () => {
    if (index === array.length - 1) {
      callback();
    }
  });
});

This does not guarantee that callback will execute after all items are processed. It only guarantees that callback will execute after the very last item is processed.

More information

Michael.

like image 44
Michael Horojanski Avatar answered Sep 19 '22 15:09

Michael Horojanski