в nodejs, как остановить цикл FOR, пока не вернется вызов mongodb

Пожалуйста, посмотрите на фрагмент кода ниже. У меня есть массив JSON-объектов, который называется «aO; stuObjList». Я хочу пройти через массив, чтобы найти конкретные объекты JSON с определенным установленным флагом, а затем сделать вызов db, чтобы получить больше данных.

Конечно, цикл FOR не ожидает возврата вызова db и достигает конца с длиной j ==. И когда возвращается вызов db, индекс 'j' находится за пределами индекса массива. Я понимаю, как работает node.js, и это ожидаемое поведение.

Мой вопрос, что за работа здесь. Как я могу достичь того, чего пытаюсь достичь? Спасибо, --su

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

Ответы на вопрос(4)

...............
...............
...............
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 строки, было бы справедливо дублировать.

 Silent User25 мая 2012 г., 05:20
@robrich ... спасибо за ответ. Но я думаю, здесь есть некоторая путаница. Я не хочу возвращаться, пока я не перебрал ВСЕ объекты в stuObjList. Из вашего кода выглядит так: как только я встретлю первый объект, который соответствует условию IF, функция вернется. Есть ли что делать?
 25 мая 2012 г., 11:02
Тогда вы правы, это решение не будет работать так хорошо, и нам нужно взглянуть на отложенный JQuery.
Решение Вопроса

& Quot;асинхронной& Quot; Это очень популярный модуль для абстрагирования асинхронных циклов и облегчения чтения / поддержки вашего кода. Например:

var async = require('async');

function getHonorStudentsFrom(stuObjList, callback) {

    var honorStudents = [];

    // The 'async. 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
});

Обратите внимание, что.для каждого() будет вызывать вашу функцию итератора для каждого элемента в stuObjList & quot; параллельно & quot; (т. е. он не будет ждать, пока одна функция итератора завершит вызов для одного элемента массива, прежде чем вызывать его для следующего элемента массива). Это означает, что вы не можете предсказать порядок, в котором будут выполняться функции итератора, или, что более важно, вызовы базы данных. Конечный результат: непредсказуемый орден чести студентов. Если порядок имеет значение, используйте.forEachSeries () функция.

 Silent User31 мая 2012 г., 04:37
@ Clint ... это сработало. большое спасибо!
 Silent User25 мая 2012 г., 05:17
@ Clint ... большое спасибо. Я попробую это и дам вам знать, как это работает.
And when the db call returns, the index 'j' is beyond the array index.

копию & quot; 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 на каждой итерации. Это состояние сохраняется внутри каждого IIFE. У вас будет столько же сохраненных состояний, сколько и для циклов for. Когда БД возвращается:

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

j_copy сохранит значение исходного j, которое оно имеет в момент

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

Я знаю, что мои навыки объяснения очень плохи, но я надеюсь, что вы понимаете, что я имею в виду.

Редактировать: Таким образом, мы используем немедленно вызванную функцию и ее область действия, чтобы сохранить отдельную частную копию j. На каждой итерации создается новый IIFE со своей собственной областью видимости. В этой области - на каждой итерации мы делаем j_copy = j. И этот j_copy может быть использован внутри IIFE без перезаписи цикла for каждый раз.

вы также можете использовать «фильтр» подчеркивания 's' метод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); });
      }
    }
  }
}

Ваш ответ на вопрос