Reputation: 505
With this document :
{
"_id" : 8,
"updatedAt" : ISODate("2016-04-13T05:07:38.905Z"),
"createdAt" : ISODate("2016-04-13T04:15:37.612Z"),
"author" : "[email protected]",
"urgency" : "slow",
"state" : "pending",
"comment" : "",
"requests" : [
{
"value" : 1,
"product" : "Slethoxynal",
"_id" : ObjectId("570dc7e91d15852f1c2ae66a")
},
{
"value" : 1,
"product" : "Thyrploxynénol",
"_id" : ObjectId("570dc7e91d15852f1c2ae66b")
}
],
"__v" : 0
}
I use this javascript function :
function closeRequest(req, res, next) {
Request.findOne({ '_id': req.body.id}, function(err, request){
debug(request);
debug("Length is %s", request.requests.length);
for(var i=0;i<request.requests.length;i++){
debug("We validate the element %s of the request %s", i, req.body.id);
console.log(i);
Material.findOne({'title': request.requests[i].product}).exec(function(err, material){
debug("Inside i is : %i",i);
debug("Product is %s", request.requests[i].product);
debug("Material found is %s", material.title);
material.amount -= request.requests[i].value;
material.save(function(err) {
if(err)
res.send(err);
});
});
}
request.state='accepted';
request.save(function(err) {
if(err)
res.send(err);
});
});
res.redirect('back');
}
And my console prints :
0
1
Inside i is 2
Then the function crashes with
TypeError: Cannot read property 'product' of undefined
At line
debug("Product is %s", request.requests[i].product);
The goal of this function is to validate one request (we put its state from 'pending' to 'accepted'). But as we do that, the stocks in the warehouse needs to be decremented (the 'amount' parameters).
It seems pretty logical that we cannot access the product property since i
had a value of 2
and there is no third element in our array.
But why is my iteration variable going out of its range ? And how can i solve that ? The fact that the console prints "Inside" only once makes me wonder if it's not another asynchronous problem.
Upvotes: 1
Views: 602
Reputation: 5926
This is a problem about async execution of your code. In particular the for loop "triggers" multiple async functions and continuous execution. After looping through your loop i===2
. As the first Material.findOne
callback gets executed requests[2] === undefined
(as requests.length === 2
) throws your error.
I'd suggest to go like this:
var updateMaterialFns = request.requests.map(function(req, i) {
return function(done) {
debug("We validate the element %s of the request %s", i, req.body.id);
console.log(i);
Material.findOne({'title': req.product}).exec(function(err, material){
if (err) {
done(err)
}
debug("Inside i is : %i",i);
debug("Product is %s", request.requests[i].product);
debug("Material found is %s", material.title);
material.amount -= request.requests[i].value;
material.save(done)
})
}
})
async.parallel(updateMaterialFns, function(err) {
if (err) {
res.send(err)
}
})
async is a node/npm module featuring many functions to deal with the execution and control flow of async programming.
Upvotes: 1