【问题标题】:Javascript how to execute code after for loop completesJavascript如何在for循环完成后执行代码
【发布时间】:2014-07-31 16:17:43
【问题描述】:

我正在尝试解决这个 js/async 场景,并且我正在尝试了解其他 js 世界如何处理这个问题。

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

编辑

这是一个更具体的示例,使用以下大多数建议进行了更新,但仍然不起作用。

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

【问题讨论】:

标签: javascript node.js asynchronous


【解决方案1】:

不使用承诺或任何其他依赖项/库,您可以简单地

function doStuff(callback) {

添加一个计数器

    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

在每次异步请求后检查

        ...when async operation is complete call
        cursorTaskComplete()
  }
}

【讨论】:

  • 这是我在没有承诺的情况下想过的方式。
  • 它绝对可以确保大量处理的安全,并且复杂性更低。
  • 没有 cursor.length 属性 - 有一个 cursor.count() 方法,但它本身就是异步的!
  • 在撰写本文时,问题中只有一个数组。此解决方案解决了 EDIT 之前提到的一般情况。
【解决方案2】:

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

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

然后用法变成:

doStuff().then(callback)

请注意,回调的调用现在如何从不触及 doStuff 函数 - 该函数现在也返回一个承诺。您现在可以注册多个回调、失败回调等,而无需修改doStuff。这称为“关注点分离”。

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

EDIT 进一步的讨论和实验已经确定.each 调用本身是异步的,并且在看到最后一行时没有向外部提供任何指示。我创建了一个 Gist 来演示此问题的解决方案。

【讨论】:

  • 对不起,我想我实际上并没有在我的 cursor.each 循环中做异步操作。只是 cursor.each 循环在我的回调方法之前没有完成。不确定这是否会影响您的回答。
  • 这似乎不起作用。 return Q.all(promises).then(callback); 仍然在 cursor.each is executed 内部的任何内容之前执行。
  • 我认为问题是需要一段时间才能达到cursor.each 的回调,所以return Q.all(promises).then(callback);cursor.eachpromises 之前执行是一个空数组,所以它只是触发回调。我不知道如何通过承诺来规避这一点。
  • 不,理论上promises 数组在调用Q.all 时已经满了(因为.each 不是异步的)。
  • @Catfish re:您的第一条评论 - 如果cursor.each 循环中没有异步代码,那么您的原始代码将可以工作。回复:您的第二条评论 - 是的,这就是承诺的工作方式。 Q.all.then 函数本身是异步的——它们立即返回。您不能在 JS 中“忙闲置”,因此您必须有一些机制不仅可以等待异步内容完成,还可以等待 doStuff 完成。 Promise 提供了这一点。
【解决方案3】:

如果你想使用 async 模块,你可以使用 async forEachSeries 函数

代码sn-p:

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

【讨论】:

    【解决方案4】:

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

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

    但没有它你可以这样做....更新

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

    这个的要点如下图...注意....当

    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 秒内完成,因此状态将相应更新。

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

    【讨论】:

    • 您的第二个解决方案与@Alnitak 的基本相同,但它不起作用的问题。它正在返回,因为代码甚至进入了cursor.each
    • 是的,类似于@Alnitak,但细微的差别在于“开始运行”与“完成异步步骤”。我为您创建了一个有效的 plnkr。请注意,状态“开始运行”在回调中。 plnkr.co/edit/27l7t5VLszBIW9eFW4Ip?p=preview
    猜你喜欢
    • 1970-01-01
    • 2021-08-10
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多