嵌套在 async.eachSeries 循环中的 async.series 终止提前发送 POST 响应

Posted

技术标签:

【中文标题】嵌套在 async.eachSeries 循环中的 async.series 终止提前发送 POST 响应【英文标题】:async.series nested in async.eachSeries loop terminates early sending POST response 【发布时间】:2015-07-25 15:26:04 【问题描述】:

我正在接收包含对象数组 (requestArray) 的 POST 调用。在响应 POST 之前,我需要按顺序通过一系列函数传递数组的对象。我选择了异步库来帮助我完成这项任务,但是我很难控制代码的执行流程。

我正在使用一个全局数组来存储每个函数的结果(responseArray)。一些函数取决于先前函数的结果。我不想使用 async.waterfall() 因为 1. 我必须重写我的代码 2. 我可能会遇到相同的早期循环终止问题。下面是我的代码有问题的代码。

app.post('/test', function(req, res) 
  var importArray = req.body;
  var iteration = 0;

  async.eachSeries(importArray, function(data, callback) 
    var index = importArray.indexOf(data);
    var element  = data.element;
    exportArray[index] = [];

    async.series([
      function(callback) 
        process1(data, index, callback);
      ,
      function(callback) 
        process2(element, index, callback);
      ,
      function(callback) 
        process3(element, index, callback);
      ],
      function(err, results)   
        var str = ;
        results.forEach(function(result) 
          if (result) 
              str += result + ',';
          
        );
        //callback();       // callback here = synchronous execution.
        if (index === req.body.length - 1) 
          res.send(exportArray);
        
      );
      console.log('async.eachSeries() callback iteration # = ' + iteration);
      iteration++;
      callback();           // callback here = asynchronous execution.
  , function(err)
      if( err ) 
        console.log('Error');
       else 
        console.log('All data has been processes successfully.');
      
  );
);

async.series() 中的每个函数都返回 callback(null, result)。在 process1() 返回其回调后, async.eachSeries() 跳转到之前的下一个数组条目,这是理想的。但是, async.eachSeries() 在所有 async.series() 结果返回之前执行一个 POST 响应。在我发送 POST 响应之前,我如何修改我的代码,以便 async.eachSeries() 在从 process1-3 返回所有 importArray 结果 (exportArray) 后完成执行?

【问题讨论】:

年纪大了一点,我意识到提前终止async.eachSeries()循环的问题是由于我在async.series()方法的末尾插入了异步方法results.forEach()的错误。而不是results.forEach(),我应该使用像for()循环这样的同步方法。 【参考方案1】:

为了便于遵循代码的异步特性,我建议稍微重命名回调。还要等到每个系列完成移动 res.sendeachSeries 最终回调传递 results

这是更新后的代码。

app.post('/test', function(req, res) 
  var importArray = req.body;
  var iteration = 0;

  async.eachSeries(importArray, function(data, next) 
    var index = importArray.indexOf(data);
    var element  = data.element;
    exportArray[index] = [];

    async.series([
      function(cb) 
        process1(data, index, cb);
      ,
      function(cb) 
        process2(element, index, cb);
      ,
      function(cb) 
        process3(element, index, cb);
      ],

      function(err, results)   
        var str = ;
        results.forEach(function(result) 
          if (result) 
              str += result + ',';
          
        );

        console.log('async.eachSeries() callback iteration # = ' + iteration);
        iteration++;
        next(null, results);      
      );     

  , function(err, results)
      if(err) 
        return console.log('Error');
       

      res.send(exportArray);
      console.log('All data has been processes successfully.');
  );
);

【讨论】:

感谢 Bulkan 的修改。 next(null, results) 抛出错误,因为 results 不在范围内。另外,我应该有 2 个res.send(exportArray) 语句吗? 我能够实施您的更改并且它工作正常。但是,它确实是同步运行的。我正在复制我必须异步运行的代码。【参考方案2】:

在@Bulkan 的帮助和修修补补之后,在我的老朋友“flag”的帮助下,我得到了异步运行的代码。代码如下:

app.post('/test', function(req, res) 
  var importArr = req.body;
  var iteration = 0;
  var flag      = false;

  async.eachSeries(importArr, function(data, cb) 
    var index        = importArr.indexOf(data);
    var element      = data.element;
    exportArr[index] = [];

    async.series([
      function(cb) 
        process1(data, index, cb);
      ,
      function(cb) 
        process2(element, index, cb);
      ,
      function(cb) 
        process3(element, index, cb);
      ],

      function(err, results)   
        var str = ;
        results.forEach(function(result) 
          if (result) 
              str += result + ',';
          
        );
        iteration++;
        if (iteration === req.body.length) 
          flag = true;
          res.send(exportArr);
         
      );
      console.log('async.eachSeries() callback iteration # = ' + iteration);

      if (iteration < req.body.length) 
        cb();
       else if (flag)  
        cb();
      
  , function(err)
      if (err) 
        console.log('Error');
       else 
        console.log('All data has been processes successfully.');
      
  );
);

【讨论】:

以上是关于嵌套在 async.eachSeries 循环中的 async.series 终止提前发送 POST 响应的主要内容,如果未能解决你的问题,请参考以下文章

嵌套节点 async.eachSeries

node.js 中的 async.eachSeries

node.js 中的 async.eachSeries

node.js async.eachSeries 过早调用最终回调

如何在caolan async中使用await?

ES6 Promises - 类似 async.each 的东西?