在 node.js 中一次遍历一个包含 50 个项目的数组

Posted

技术标签:

【中文标题】在 node.js 中一次遍历一个包含 50 个项目的数组【英文标题】:Iterate through an array in blocks of 50 items at a time in node.js 【发布时间】:2018-03-19 19:54:36 【问题描述】:

我是 node.js 的新手,目前正在尝试编写数组迭代代码。我有一个包含 1,000 个项目的数组 - 由于服务器负载问题,我想一次迭代 50 个项目。

我目前使用如下所示的 forEach 循环(我希望将其转换为上述块迭代)

   //result is the array of 1000 items

   result.forEach(function (item) 
     //Do some data parsing
     //And upload data to server
    );

任何帮助将不胜感激!

更新(响应回复)

async function uploadData(dataArray) 
    try 
        const chunks = chunkArray(dataArray, 50);
        for (const chunk of chunks) 
            await uploadDataChunk(chunk);
        
     catch (error) 
        console.log(error)
        // Catch en error here
    


function uploadDataChunk(chunk) 
    return Promise.all(
        chunk.map((item) => 
            return new Promise((resolve, reject) => 
               //upload code
                
            )
        )
    )

【问题讨论】:

这听起来像XY problem 我假设您在循环中处理的任务是异步执行的。因此主要问题不是循环,而是异步任务立即返回,因此同时执行了 1000 个异步任务,对吧? @charlietfl 是和否,我知道我们的数据库可以处理的每秒写入请求数,blocks 功能可以让我保持在这些限制内 @Robert 这正是难题,我要求上传是异步的,但要求它限于我们数据库的写入限制能力 【参考方案1】:

您应该首先将您的数组拆分为 50 个块。然后您需要一个一个地发出请求,而不是一次。 Promise 可用于此目的。

考虑这个实现:

function parseData()   // returns an array of 1000 items

async function uploadData(dataArray) 
  try 
    const chunks = chunkArray(dataArray, 50);
    for(const chunk of chunks) 
      await uploadDataChunk(chunk);
    
   catch(error) 
    // Catch an error here
  


function uploadDataChunk(chunk) 
  // return a promise of chunk uploading result


const dataArray = parseData();
uploadData(dataArray);

使用 async/await 将在底层使用 Promise,因此await 将等待当前块上传,然后才会上传下一个块(如果没有发生错误)。

这里是我对 chunkArray 函数实现的建议:

function chunkArray(array, chunkSize) 
  return Array.from(
     length: Math.ceil(array.length / chunkSize) ,
    (_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize)   
  );


注意:此代码使用 ES6 特性,因此最好使用 babel / TypeScript。

更新

如果您创建多个异步数据库连接,只需使用一些数据库池工具。

更新 2

如果你想异步更新所有的chunk,当chunk上传后开始上传另一个,你可以这样:

function uploadDataChunk(chunk) 
  return Promise.all(
    chunk.map(uploadItemToGoogleCloud) // uploadItemToGoogleCloud should return a promise
  );

【讨论】:

我从 50 个项目块以 forEach 格式运行我的数据库上传,一旦 forEach 完成对块的迭代,我如何返回一个承诺?非常感谢尤里! 你的意思是uploadDataChunk里面的forEach?您是否单独上传每个项目?你的更新函数返回什么?是异步的吗? 是的,我使用 Google Cloud Firestore,它可以在每次上传文件时返回 Promise。但它不能以这种格式进行批量写入。我必须在 forEach 块中分别上传每个项目,并且由于速度原因以异步方式。计数器可以工作,但我不确定如何使用“等待”行实现完成以及触发下一个块上传所需的承诺 如果您想异步上传当前块中的所有 50 个项目,请查看我的更新答案(更新 2)。如果要同步上传每个项目,则不要分块。 我已经尝试了您的新代码,并且第一个块上传完美 - 但是该功能随后完成并且不会继续到下一个块。我已经发布了我的代码作为我的问题的更新。有任何想法吗?感谢您的帮助!【参考方案2】:

您可以按如下所需的块大小对数组进行分块;

function chunkArray(a,s) // a: array to chunk, s: size of chunks
  return Array.from(length: Math.ceil(a.length / s))
              .map((_,i) => Array.from(length: s)
                                 .map((_,j) => a[i*s+j]));


var arr = Array(53).fill().map((_,i) => i); // test array of 53 items
console.log(chunkArray(arr,5))              // chunks of 5 items.
.as-console-wrapper
max-height: 100% ! important;

【讨论】:

您的代码运行良好,但另一个答案通过数据库上传和承诺提供了一些额外的调试。尽管如此,还是投了赞成票! @Hendies 谢谢...但我在这里展示的只是分块基础架构。然后你所要做的就是Promise.all() 块和Promise.all() 主数组。基本上就是这么酷..! 添加一个过滤器会很好——在示例代码中,最后一个数组中有 2 个undefined【参考方案3】:

有一个曾经非常流行的库:async.js(不要与 async 关键字混淆)。我仍然认为这有时是更清洁的方法,尽管这些天使用async/await 我倾向于在for 循环中手动完成。

异步库实现了许多异步流控制设计模式。对于这种情况,您可以使用eachLimit:

const eachLimit = require('async/eachLimit');

eachLimit(result, 50,
    function (item) 
        // do your forEach stuff here
    ,
    function (err) 
        // this will be called when everything is completed
    
);

或者,如果您愿意,您可以使用 promisified 版本,以便您可以await 循环:

const eachLimit = require('async/eachLimit');

async function processResult (result) 
    // ...

    try 
        await eachLimit(result, 50, function (item) 
            // do your forEach stuff here
        );
    
    catch (err) 
        // handle thrown errors
    

在这种特定情况下,手动批处理操作并使用await 在批处理之间暂停非常容易,但async.js 库包含一组丰富的函数,这些函数很有用。即使使用async/await,其中一些仍然很难做到,例如whilst(异步while)、retryforever 等(参见文档:https://caolan.github.io/async/v3/docs.html

【讨论】:

以上是关于在 node.js 中一次遍历一个包含 50 个项目的数组的主要内容,如果未能解决你的问题,请参考以下文章

在node.js中一次读取一行文件?

excel怎么计算一列中一共有多少个项

在反应jsx中一次遍历一个数组

如何在zsh中一次遍历一个单词

50.000 个并发连接的 Node.js 系统要求

保持生成过程中的 Node.js 结果有序