编辑:我用更简单的代码和How to avoid a race condition in Nodejs and Mongoose app?中的更多信息重写了这个问题。我将其保留为原样,以便人们可以根据需要参考详细的代码。

我有一个带有子文档的猫鼬节点设置。我需要调用函数以使用setTimeout根据某些条件更新数字。但是,有时由于回调未快速返回而进入竞争状态。这是一个示例方法。

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);
         }
    })
}


我简化了代码以解释我想要的。如何在不发生争用条件的情况下成功调用setTimeout?

最佳答案

未经测试,但您可以尝试以下方法:

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);
      })
    })
}

10-03 01:00