EDIT: I rewrote this question with simpler code and more information in How to avoid a race condition in Nodejs and Mongoose app?. I left this as is so people can refer to detailed code if they wish.
I have a Mongoose, Node setup with sub-documents. I need to call a function to update a number based on some conditions using setTimeout. However, sometimes it gets into a race condition because of callbacks not returning quickly. Here is a sample method....
function myProcessPump(){
//Get top level documents based on some condition
UserInfo
.where('some_condition').lte(new Date())
.exec(function(err, UserInfoRetArray){
if (!err) {
if (UserInfoRetArray.length > 0){
//Iterate thru the top level documents
for (var i = 0; i < UserInfoRetArray.length; i++){
//Iterate thru sub-documents
for (var j = 0; j < UserInfoRetArray[i].someItems.length; j++){
// do some work...
do_work(UserInfoRetArray[i].someItems[j]);
//update status in db
set['someItems.$.some_condition'] = new Date((new Date()).getTime() + 10000) ;
UserInfo.update({_id: UserInfoRetArray[i]._id, "someItems._id":UserInfoRetArray[i].someItems[j]._id},
{$set: set}, function(err, numAffected) {
//set timeout so myProcessPump is called again.
setTimeout(myProcessPump, 1000);
});
}
}
} else {
//set timeout so myProcessPump is called again.
setTimeout(myProcessPump, 1000);
// I suspect this gets called when the previous call back does not
// complete in time causing the mulitple timers running in parallel.
}
} else {
//set timeout so myProcessPump is called again.
setTimeout(myProcessPump, 1000);
}
})
}
I have simplified the code to explain what I want. How do I successfully call the setTimeout without a race condition happening?
Not tested but you might try this:
function myProcessPump() {
//Get top level documents based on some condition
UserInfo
.where('some_condition').lte(new Date())
.exec(function(err, UserInfoRetArray) {
//Wrap everything in an async function with a callback
var processUser = function(done) {
if (!err) {
if (UserInfoRetArray.length > 0) {
//Iterate thru the top level documents
for (var i = 0; i < UserInfoRetArray.length; i++) {
//Iterate thru sub-documents
for (var j = 0; j < UserInfoRetArray[i].someItems.length; j++) {
// do some work...
do_work(UserInfoRetArray[i].someItems[j]);
//update status in db
set['someItems.$.some_condition'] = new Date((new Date()).getTime() + 10000);
UserInfo.update({
_id: UserInfoRetArray[i]._id,
"someItems._id": UserInfoRetArray[i].someItems[j]._id
}, {
$set: set
}, function(err, numAffected) {
done();
});
}
}
} else {
done();
}
} else {
done();
}
}
//Call async function and when done redo the whole thing
processUser(function() {
//probably done need the timeout and can just call myProcessPump unless you really need a delay
myProcessPump();
//setTimeout(myProcessPump, 1000);
})
})
}