NodeJS,如何强制异步 for 循环在传递到下一次迭代之前等待 HTTP 请求解决,这样我们就不会收到 EMFILE 错误?

Posted

技术标签:

【中文标题】NodeJS,如何强制异步 for 循环在传递到下一次迭代之前等待 HTTP 请求解决,这样我们就不会收到 EMFILE 错误?【英文标题】:NodeJS, How to force the async for loop to wait the HTTP request to resolve before passing to the next iteration so we don't get the EMFILE error? 【发布时间】:2018-12-09 21:08:13 【问题描述】:

我正在尝试集成到自定义 API 中,我从数据库中获取了一些行,然后对于连接到 API 的每一行。 问题是当数据库返回 3000 行或更少时,API 返回正确的响应,当我 POST 超过 3000 行时,我得到以下信息:

EMFILE ERROR -> OS won't allow the opening of more sockets

我相信 API 不允许超过 3000 个 HTTP 请求 我尝试了 promise/async-await 方法来等待 HTTP 请求以失败告终,这是我添加睡眠功能以等待 200 毫秒或更长时间的唯一一次工作

这是我原来的功能:

    async function send_data(rows)
    let a=0;    
    log.info("Emails to integrate: "+rows.length);
    if(rows.length)
      for(const row of rows)
        log.info(row.email+" --- "+row.name+" --- "+row.id);
        await integrate_data(row,a);
        //await sleep(50);
        a++;
      
      log.info("Finished integration, setting last ID");
     else 
      log.info("No data to integrate");
    


function integrate_data(row,counter)
        var options = 
          "method": "POST",
          "hostname": "API-URL",
          "path":"PATH/EMAIL/"+row.email,
          "headers": 
            "Content-Type": "application/json"
          
        ;
        var req = http.request(options, function (res) 
          var chunks = [];

          res.on("data", function (chunk) 
            chunks.push(chunk);
          );

          res.on("end", function () 
            var body = Buffer.concat(chunks);
            log.info(body.toString());
          );
        );

        let obj;
          obj =  
            key1: row.name,
            key2: row.id, 
            key3: row.iterationNumber
         

        req.write(JSON.stringify(obj));
        req.end();


function sleep(ms) 
  return new Promise(resolve => setTimeout(resolve, ms));

是否无论如何要等待 HTTP 事务完成然后进入下一次迭代,以便该函数可以打开连接、推送数据、关闭连接然后发出信号以进行下一次迭代?

提前致谢

【问题讨论】:

【参考方案1】:

await integrate_data(...) 没有按照你的想法做。您无需等待请求完成,您的代码就会执行

req.write(JSON.stringify(obj)); 
req.end();

然后进行下一次迭代。

要使await 按预期工作,integrate_data 必须返回一个Promise,并且应该在请求完成时解决。

function integrate_data(row, counter) 
    var options = 
        "method": "POST",
        "hostname": "API-URL",
        "path": "PATH/EMAIL/" + row.email,
        "headers": 
            "Content-Type": "application/json"
        
    ;

    return new Promise((resolve, reject) => 

      var req = http.request(options, function(res) 
          var chunks = [];

          res.on('error', reject);

          res.on("data", function(chunk) 
              chunks.push(chunk);
          );

          res.on("end", function() 
              var body = Buffer.concat(chunks);
              log.info(body.toString());
              resolve(body.toString()); // resolve promise
          );
      );

      let obj;
      obj = 
          key1: row.name,
          key2: row.id,
          key3: row.iterationNumber
      

      req.write(JSON.stringify(obj));
      req.end();
    );

【讨论】:

非常感谢@Marcos Casagrande,它的工作原理很有魅力 确实,我对 SO 以及 NodeJS 和 Async/Await/Promise 概念都是新手。我接受了答案,再次感谢你,伙计

以上是关于NodeJS,如何强制异步 for 循环在传递到下一次迭代之前等待 HTTP 请求解决,这样我们就不会收到 EMFILE 错误?的主要内容,如果未能解决你的问题,请参考以下文章

在for循环中调用异步函数

在赛普拉斯的 for 循环中等待异步方法

强制异步任务按顺序运行

nodejs所用的概念(同步,异步,事件驱动,事件循环等)通俗解释

如何在Matlab的for循环中移动到下一个迭代

node.js里的forEach也是异步的吗