关于node.js:Javascript如何在for循环完成后执行代码

Javascript how to execute code after for loop completes

我正在尝试解决这个js /异步场景,我想知道js世界的其余部分如何处理这个问题。

1
2
3
4
5
6
7
8
function doStuff(callback) {

  cursor.each(function(err, blahblah) {
    ...doing stuff here takes some time
  });

  ... Execute this code ONLY after the `cursor.each` loop is finished
  callback();

编辑

这是一个更具体的例子,使用下面的大多数建议进行了更新,但仍然无效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function doStuff(callback) {

  MongoClient.connect(constants.mongoUrl, function(err, db) {

    var collection = db.collection('cases2');
    var cursor = collection.find();

    var promises = [];  // array for storing promises

    cursor.each(function(err, item) {

      console.log('inside each'); // NEVER GETS LOGGED UNLESS I COMMENT OUT THIS LINE: return Q.all(promises).then(callback(null, items));

      var def = Q.defer();        // Create deferred object and store
      promises.push(def.promise); // Its promise in the array

      if(item == null) {
        return def.resolve();
      }

      def.resolve();  // resolve the promise
    });

    console.log('items'); // ALWAYS GETS CALLED
    console.log(items);

    // IF I COMMENT THIS LINE OUT COMPLETELY,
    // THE LOG STATEMENT INSIDE CURSOR.EACH ACTUALLY GETS LOGGED
    return Q.all(promises).then(callback(null, items));
  });
}


你可以简单地使用promises或任何其他依赖/库

1
function doStuff(callback) {

加一个柜台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    var cursor = new Array(); // init with some array data
    var cursorTasks = cursor.length;

    function cursorTaskComplete()
    {
        cursorTasks--;

        if ( cursorTasks <= 0 ) {
            // this gets get called after each task reported to be complete
            callback();
        }
    }

    for ( var i = 0; i < cursor.length; i++ ) {
        ...doing stuff here takes some time and does some async stuff

检查每个异步请求后

1
2
3
4
        ...when async operation is complete call
        cursorTaskComplete()
  }
}


在不知道您在cursor.each循环中进行的异步调用的细节的情况下,我将假设您每次调用其中的函数完成其异步任务时都能够调用回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
function doStuff() {
    var promises = [];  // array for storing promises

    cursor.each(function(err, blahblah) {
        var def = Q.defer();        // create deferred object and store
        promises.push(def.promise); // its promise in the array

        call_async_function(..., def.resolve);  // resolve the promise in the async function's callback
    });

    // pass the array to Q.all, only when all are resolved will"callback" be called
    return Q.all(promises);
}

然后用法变为:

1
doStuff().then(callback)

注意回调的调用现在永远不会触及doStuff函数 - 该函数现在也返回一个promise。您现在可以注册多个回调,故障回调等,而无需修改doStuff。这被称为"关注点分离"。

[注意:所有以上基于Q promises库 - https://github.com/kriskowal/q]

编辑进一步的讨论和实验已经确定.each调用本身是异步的,并且在看到最后一行时没有给出外部指示。我已经创建了一个Gist来演示解决这个问题的方法。


如果要使用async模块执行此操作,可以使用async forEachSeries函数

代码段:

1
2
3
4
5
6
7
8
9
10
11
function doStuff(callback) {

  async.forEachSeries(cursor, function(cursorSingleObj,callbackFromForEach){
      //...do stuff which takes time
      //this callback is to tell when everything gets over execute the next function
      callbackFromForEach();
  },function(){
     //over here the execution of forEach gets over and then the main callback is called
    callback();
  });
}

在我看来,优雅/理想的解决方案就是拥有类似的东西

1
 cursor.each(........).then( function() { ....your stuff});

但没有它,你可以做到这一点....更新

http://plnkr.co/edit/27l7t5VLszBIW9eFW4Ip?p=preview

这个的要点如下所示......注意......当

1
2
3
4
5
6
7
8
9
10
11
var doStuff = function(callback) {
      cursor.forEach(function(cursorStep) {
        var deferred = $q.defer();
        var promise = deferred.promise;
        allMyAsyncPromises.push(promise);
        cursorStep.execFn(cursorStep.stepMeta);
        promise.resolve;
      });

      $q.when(allMyAsyncPromises).then(callback);
}

点击开始按钮后等待几秒钟......异步任务已经模拟完成,并在5秒内完成,因此状态将相应更新。

无法访问真正的光标对象..我不得不求助于虚拟光标和数组。