claursen claursen - 2 months ago 6
Node.js Question

Push objects to Array in nested loop

I'm having difficulties sending a populated array after two nested loops has completed iterating. I'm using the Async npm library and trying to use the async.forEach completion callback to send the entire array. The inner array iterates over 5 objects, which constitutes a "course" - afterwhich the course object is saved to a person object and there's multiple course objects per person object.

var self = this,
person = [],
course = [];


async.forEach(elements.value, function (element, callback1) {
self.elementIdElements(element.ELEMENT, 'td').then(function (rows) {
async.forEach(rows.value, function (cell, callback2) {
self.elementIdText(cell.ELEMENT).then(function (res) {
course.push(res.value);
callback2();
});
}, callback1);
person.push(course);
course = [];
});
}, function (err) {
res.send('grades: ' + JSON.stringify(person));
});


However, it calls the sendResponse in the middle of iterating through the arrays and I simply cannot phantom why this is happening, it should call it after iterating through the whole thing. The scope and asynchronous calls is confusing

Thanks in advance,
Chris

UPDATE - SOLUTION



I finally figured out the scope of the callbacks with the following code:

var self = this,
person = [],
course = [];

async.forEach(elements.value, function (element, callback1) {
self.elementIdElements(element.ELEMENT, 'td').then(function (rows) {

async.forEach(rows.value, function (cell, callback2) {
self.elementIdText(cell.ELEMENT).then(function (res) {
course.push(res.value);
callback2();
});

}, function (err) {
person.push(course);
course = [];
callback1();
});

});
}, function (err) {
res.send('grades: ' + JSON.stringify(person));
});

Answer

You must nest your callbacks inside each other. With the async library, the last argument of many of the calls is the "final callback", which lets you know when everything is done.

You're outer async.forEach looks good, where the final callback of sendResponse is the final argument.

Change the inner async.forEach so callback is the final argument. async.forEach(rows.value, getCourseData, callback)

I might also suggest more descriptive callback names so you can easily keep track ("innerCallback" "outerCallbacl" or "rowsCallbacl" ... whatever you want)