【问题标题】:using async correctly (with forEach)正确使用异步(使用 forEach)
【发布时间】:2013-12-17 10:58:44
【问题描述】:

所以我有一个很好的嵌套异步代码块一起运行,这一切似乎都很好,除了当我到达它的末尾时。我在系列块中的最后一个函数是 forEach: 然后进入 async.parallel

设法追踪以下未按顺序运行的块:

async.forEach(array, function(elem, callback) {
      async.parallel([
          function(callback) {
              database-call-A(elem, unction(err, data)  {
                    if(err){
                        console.log("error on first parallel");
                        callback({err: false}); // will break out
                    } else {
                        elem.c = data;
                        callback();
                    }
              });
          },
          function(callback) {
               database-call-B(elem, function(err, data)  {
                    if(err){
                        console.log("error on first parallel");
                        callback({err: false}); // will break out
                    } else {
                        elem.c = data;
                        callback();
                    }
              });
          } 
       ]); // end async.parallel 

       // if forEach needs a callback after every iteration (which I think it does?)
       console.log("PRINTS N TIMES - ONCE FOR EVERY ITERATION");
       callback(); // both parallel functions have run, call back forEach

 }); // end forEach

 console.log("Donions - prints when finished");

当我通过到处抛出打印语句来测试这段代码时,我注意到“PRINTS N TIMES ...”运行了 N 次,然后我得到“Donions ..”然后我做某事();和其他东西();开始在我的 async.parallel 中被调用。

为什么我的 forEach 在不等待来自 async.parallel 的回调的情况下进行迭代?

【问题讨论】:

    标签: javascript asynchronous foreach


    【解决方案1】:

    首先,没有async.forEach,但有async.each 函数。 async.parallel 函数将同时执行所有任务,然后执行回调(如果已定义)。

    在您的源代码中,callback 函数将在 async.parallel 执行后立即执行,而不是在它的所有函数都返回之后。我建议你阅读documentation

    如果要在调用所有并行函数的回调后执行callback,则应将forEach 的回调作为第二个参数传递给async.parallel 函数,位于函数数组之后。此外,您可能应该将回调传递给databaseCallAdatabaseCallB,从而使它们在完成时调用它们的回调,这样它们就不会过早执行。

    async.each(array, function(elem, callback) {
        async.parallel([
            function(cb) {
                databaseCallA(cb);
                // if you have called the cb here, the `async.parallel` function would "think" that the databaseCallA has finished, even though it may not have started yet.
            },
            function(cb) {
                databaseCallB(cb):
            } 
        ], callback);
    });
    

    您应该修改您的数据库调用函数以接受回调参数并让它们在完成工作后调用回调。

    异步调用的目的是在等待您的作业完成时为其他作业释放资源 - 您的代码将继续执行,但您可以使用回调或某种基于事件的通知来通知其他人您的异步工作已经完成。

    编辑

    在所有并行调用完成后执行某事:

    async.each(array, function(elem, callback) {
        async.parallel([
            function(cb) {
                // I use the setTimeout function to intentionally
                // delay the response
                setTimeout(function() {
                    // the first cb parameter is error, the second is result 
                    // in case there was no error
                    cb(undefined, 'a');
                }, 500);
            },
            function(cb) {
                setTimeout(function() {
                    cb(undefined, 'b');
                }, 300);
            },
            function(cb) {
                setTimeout(function() {
                    cb(undefined, 'c');
                }, 800);
            }
        ], function(err, results) {
            // this will be executed only after all three cb's are executed. In
            // this case, after about 800ms. The results variable will be an 
            // array containing the resuts passed to each cb. In this case it
            // will  be ['a', 'b', 'c'].
    
            // you could call the main callback here
            callback();
        });
    }, function(err) {
        // this callback will be executed either after everything was done,
        // or if an error has occurred.
        if (err) {
            handleError(err);
            return;
        }
    
        // this should be executed after all `each` and `parallel` calls
        // have finished. In this case, it should also be after about 800ms
        // from the start, since everything was executed in parallel
        console.log('finished');
    
        // now you are sure everything was done, do something afterwards
        after();
    });
    
    // this will be executed immediately so don't put anything here
    // that depends on the outcome of the async calls above
    console.log('test');
    

    【讨论】:

    • 你是对的!我在这里简化了一点,检查更新以获取更多信息(我的数据库调用只有在它们返回时才会回调,所以我们在这方面做得很好) - 我很确定 forEach 实际上是一个函数,考虑到它在我的电脑,但我想知道为什么我在 API 上找不到它 - 让我们看看每个人是否做了任何事情..
    • 那么你的问题到底是什么?
    • 这是来自文档:Note, that since this (each) function applies the iterator to each item in parallel, there is no guarantee that the iterator functions will complete in order. 你可以试试eachSeries
    • 我不需要它们以任何顺序运行 :) 只需要在它之外的调用之前并行完成(但在 .each 的最后)。无论如何,我会尝试使用不同的迭代器,谢谢。
    【解决方案2】:

    我不确定这个答案,但根据异步文档,我认为你必须这样做:

    async.forEach(array, function(elem, callback) {
        async.parallel([
          function(callback) {
              database-call-A();
              callback();
          },
          function(callback) {
              database-call-B():
              callback(); // callback for second parallel function
          } 
       ], function(err,result) {
             // this code is executed when paralles functions ends
             console.log("PRINTS N TIMES - ONCE FOR EVERY ITERATION");
             callback(); 
       }); // end async.parallel 
    
     }); // end forEach
    

    问候。

    【讨论】:

    • 数据库调用(应该)是异步的,在这种情况下,您的回调将在数据库调用实际完成某些工作之前执行。
    猜你喜欢
    • 1970-01-01
    • 2019-02-07
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 2018-06-14
    • 2019-03-03
    相关资源
    最近更新 更多