簡體   English   中英

在nodejs中,如何在mongodb調用返回之前停止FOR循環

[英]in nodejs, how to stop a FOR loop until mongodb call returns

請查看下面的代碼段。 我有一個名為'stuObjList'的JSON對象數組。 我想循環通過數組來查找具有特定標志集的特定JSON對象,然后進行數據庫調用以檢索更多數據。

當然,FOR循環不等待db調用返回並以j == length到達結尾。 當db調用返回時,索引'j'超出了數組索引。 我理解node.js是如何工作的,這是預期的行為。

我的問題是,這里的工作是什么。 我怎樣才能實現我想要實現的目標?

...............
...............
...............
else
{
  console.log("stuObjList.length: " + stuObjList.length);
  var j = 0;
  for(j = 0; j < stuObjList.length; j++)
  {
    if(stuObjList[j]['honor_student'] != null)
    {     
      db.collection("students").findOne({'_id' : stuObjList[j]['_id'];}, function(err, origStuObj)
      {
        var marker = stuObjList[j]['_id'];
        var major = stuObjList[j]['major'];
      });
    }

    if(j == stuObjList.length)
    {
      process.nextTick(function()
      {
        callback(stuObjList);
      });
    }
  }
}
});

async ”是一個非常流行的模塊,用於抽象異步循環並使代碼更易於讀取/維護。 例如:

var async = require('async');

function getHonorStudentsFrom(stuObjList, callback) {

    var honorStudents = [];

    // The 'async.forEach()' function will call 'iteratorFcn' for each element in
    // stuObjList, passing a student object as the first param and a callback
    // function as the second param. Run the callback to indicate that you're
    // done working with the current student object. Anything you pass to done()
    // is interpreted as an error. In that scenario, the iterating will stop and
    // the error will be passed to the 'doneIteratingFcn' function defined below.
    var iteratorFcn = function(stuObj, done) {

        // If the current student object doesn't have the 'honor_student' property
        // then move on to the next iteration.
        if( !stuObj.honor_student ) {
            done();
            return; // The return statement ensures that no further code in this
                    // function is executed after the call to done(). This allows
                    // us to avoid writing an 'else' block.
        }

        db.collection("students").findOne({'_id' : stuObj._id}, function(err, honorStudent)
        {
            if(err) {
                done(err);
                return;
            }

            honorStudents.push(honorStudent);
            done();
            return;
        });
    };

    var doneIteratingFcn = function(err) {
        // In your 'callback' implementation, check to see if err is null/undefined
        // to know if something went wrong.
        callback(err, honorStudents);
    };

    // iteratorFcn will be called for each element in stuObjList.
    async.forEach(stuObjList, iteratorFcn, doneIteratingFcn);
}

所以你可以像這樣使用它:

getHonorStudentsFrom(studentObjs, function(err, honorStudents) {
    if(err) {
      // Handle the error
      return;
    }

    // Do something with honroStudents
});

注意.forEach()將為stuObjList中的每個元素“並行”調用你的迭代器函數(即,在下一個數組元素上調用它之前,它不會等待一個迭代器函數完成對一個數組元素的調用)。 這意味着您無法真正預測迭代器的功能 - 或者更重要的是數據庫調用 - 的運行順序。 最終結果:不可預知的榮譽學生順序。 如果訂單很重要,請使用.forEachSeries()函數。

啊異步思考的美麗和挫折啊。 嘗試這個:

...............
...............
...............
else
{
  console.log("stuObjList.length: " + stuObjList.length);
  var j = 0, found = false, step;
  for(j = 0; j < stuObjList.length; j++)
  {
    if(stuObjList[j]['honor_student'] != null)
    {     
      found = true;
      step = j;
      db.collection("students").findOne({'_id' : stuObjList[j]['_id'];}, function(err, origStuObj)
      {
        var marker = stuObjList[step]['_id']; // because j's loop has moved on
        var major = stuObjList[step]['major'];
        process.nextTick(function()
        {
          callback(stuObjList);
        });
      });
    }

  }
  if (!found) {
    process.nextTick(function()
    {
      callback(stuObjList);
    });
  }
}
});

如果您發現“當我完成”步驟變得復雜,請將它們提取到另一個函數,然后從每個點調用它。 在這種情況下,因為它只有2行,所以復制似乎是公平的。

根據要求,您還可以使用下划線的“過濾”方法http://documentcloud.github.com/underscore/#filter

var honor_students = _.filter(stuObjList, function(stud) { return stu['honor_student'] != null });
if (honor_students.length === 0) {
  process.nextTick(function() { callback(stuObjList); });
} else {
  var honor_students_with_more_data = [];
  for (var i = 0; i < honor_students.length; i++) {
    db.collection("students").findOne({'_id' : honor_students[i]['_id'];}, function(err, origStuObj) {
      // do something with retrieved data
      honor_students_with_more_data.push(student_with_more_data);
      if (honor_students_with_more_data.length === honor_students.length) {
        process.nextTick(function() { callback(stuObjList); });
      }
    }
  }
}
And when the db call returns, the index 'j' is beyond the array index.

在我看來,你需要在每次循環迭代中獲取j的“副本”。 你可以用閉包來做這件事。

if(stuObjList[j]['honor_student'] != null)
{

    (function(j_copy){
        db.collection("students").findOne({'_id' : stuObjList[j_copy]['_id'];}, function(err, origStuObj)
        {
            var marker = stuObjList[j_copy]['_id'];
            var major = stuObjList[j_copy]['major'];
        });
    })(j)

}

這樣你就可以在每次迭代時保存j`s狀態。 此狀態保存在每個IIFE內。 您將擁有盡可能多的已保存狀態 - 作為for循環。 當DB返回時:

var marker = stuObjList[j_copy]['_id'];

j_copy將保留原始j的值,它在當前時刻具有

if(stuObjList[j]['honor_student'] != null)

我知道我的解釋技巧非常糟糕,但我希望你能理解我的意思。

編輯:這種方式我們使用立即調用的函數及其范圍來保持j的單獨私有副本。 在每次迭代中,使用自己的私有范圍創建新的IIFE。 在這個范圍內 - 在每個迭代上我們做j_copy = j。 並且這個j_copy可以在IIFE中使用,而不必每次都被for循環覆蓋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM