user6582640 user6582640 - 3 months ago 7
Node.js Question

Iterate save in Node.JS across an array

I couldn't use a simple for loop because request.save is a function. So I tried forEach. It works perfectly! Until I add in the request.save part and I get the following error message that breaks my app.

Error: Can't set headers after they are sent.

exports.submit = function (req, res) {


Person.find({
cellPhone: req.body.phone
}).exec(function (err, people) {
people.forEach(saveRequest);
}

function saveRequest(item, index) {

var request = new Requests();
request.start = req.body.start.value;
request.finish = req.body.finish.value;
request.phone = req.body.phone;
request.offDay = req.body.date;

request.user = people[index]._id;
request.name = people[index].name;
request.group = people[index].group;

request.save(function (err) {
if (err) {
console.log('request.save');
return res.status(400);
} else {
// Remove sensitive data before login
//user.password = undefined;
//user.salt = undefined;
console.log(request);
res.json(request);
}
});
}
});

Answer

The problem is when you perform the .save() you pass an anonymous function that complete the response in case of error.

So you finish on the first save event error.

You should complete the response outside the save callback.

Maybe use events to sync your code, or better the generators.

Before your forEach loop:

let savedResponses = [];
let savedErrors = [];
...

Then your savedRequest:

function saveRequest(item, index) {

    var request = new Requests();
    request.start = req.body.start.value;
    request.finish = req.body.finish.value;
    request.phone = req.body.phone;
    request.offDay = req.body.date;

    request.user = people[index]._id;
    request.name = people[index].name;
    request.group = people[index].group;

    request.save(function (err) {
        if (err) {
            console.log('request.save error');
            savedErrors.push(err);
            // return res.status(400);
        } else {
            // Remove sensitive data before login
            //user.password = undefined;
            //user.salt = undefined;
            console.log(request);
            savedResponses.push(request);

        }
    });

}

Then after the forEach loop, you should wait the end of the asynchronous staff in the .save() callbacks.

You could use the event package or the generators or the promise pattern.

It depend on the version of your node.

When you have the code synched you could just complete your response checking for errors first:

if (savedErrors.length > 0) {
    res.status = 400;
    // ... report errors
}

Or just complete the response with the savedResponses.

Comments