简体   繁体   English

Node.js - 希望在循环中对方法进行 5 次并行调用

[英]Node.js - want 5 parallel calls to a method in a loop

I have 1000 files of information in MongoDB collection.我在 MongoDB 集合中有 1000 个信息文件。 I am writing a query to fetch 1000 records and in a loop, I am calling a function to download that file to local system.我正在编写一个查询来获取 1000 条记录,并在一个循环中调用一个函数将该文件下载到本地系统。 So, it's a sequential process to download all 1000 files.因此,下载所有 1000 个文件是一个顺序过程。

I want some parallelism in the downloading process.我希望在下载过程中有一些并行性。 In the loop, I want to download 10 files at a time, meaning I want to call download function 10 times, after completing 10 file downloads I want to download next 10 files (that means I need to call download function 10 times).在循环中,我想一次下载10个文件,这意味着我想调用10次下载函数,完成10个文件下载后我想下载接下来的10个文件(这意味着我需要调用下载函数10次)。

How can I achieve this parallelism OR is there any better way to do this?我怎样才能实现这种并行性,或者有没有更好的方法来做到这一点?

I saw Kue npm, but how to achieve this?我看到了Kue npm,但是如何实现呢? By the way I am downloading from FTP, so I am using basic-ftp npm for ftp operations.顺便说一下,我是从 FTP 下载的,所以我使用basic-ftp npm 进行 ftp 操作。

The async library is very powerful for this, and quite easy too once you understand the basics.异步库在这方面非常强大,一旦你了解了基础知识也很容易。

I'd suggest that you use eachLimit so your app won't have to worry about looping through in batches of ten, it will just keep ten files downloading at the same time.我建议您使用eachLimit,这样您的应用程序就不必担心以十个为一组进行循环,它只会同时下载十个文件。

var files = ['a.txt', 'b.txt']
var concurrency = 10;

async.eachLimit(files, concurrency, downloadFile, onFinish);


function downloadFile(file, callback){

  // run your download code here
  // when file has downloaded, call callback(null)
  // if there is an error, call callback('error code')

}

function onFinish(err, results){

  if(err) {
    // do something with the error
  }

  // reaching this point means the files have all downloaded

}

The async library will run downloadFile in parallel, sending each instance an entry from the files list, then when every item in the list has completed it will call onFinish .异步库将并行运行downloadFile ,从files列表中向每个实例发送一个条目,然后当列表中的每个项目都完成时,它将调用onFinish

Without seeing your implementation I can only provide a generic answer.没有看到你的实现,我只能提供一个通用的答案。

Let's say that your download function receives one fileId and returns a promise that resolves when said file has finished downloading.假设您的下载函数接收一个 fileId 并返回一个承诺,该承诺在所述文件下载完成后进行解析。 For this POC, I will mock that up with a promise that will resolve to the file name after 200 to 500 ms.对于这个 POC,我将模拟一个承诺,它将在 200 到 500 毫秒后解析为文件名。

function download(fileindex) {
  return new Promise((resolve,reject)=>{ 
    setTimeout(()=>{ 
      resolve(`file_${fileindex}`);
    },200+300*Math.random());
  });
}

You have 1000 files and want to download them in 100 iterations of 10 files each.您有 1000 个文件,并希望以 100 次迭代下载它们,每次迭代 10 个文件。

let's encapsulate stuff.让我们封装一些东西。 I'll declare a function that receives the starting ID and a size, and returns [N...N+size] ids我将声明一个接收起始 ID 和大小并返回[N...N+size] ids 的函数

function* range(bucket, size=10) {
    let start = bucket*size, 
        end=start+size;
    for (let i = start; i < end; i++) {
        yield i;
    }
}

You should create 100 "buckets" containing a reference to 10 files each.您应该创建 100 个“存储桶”,每个存储桶包含对 10 个文件的引用。

 let buckets = [...range(0,100)].map(bucket=>{
    return [...range(bucket,10)];
 });

A this point, the contents of buckets are: A到此, buckets的内容是:

 [
   [file0 ... file9]
   ...
   [file 990 ... file 999]
 ]

Then, iterate over your buckets using for..of (which is async-capable)然后,使用for..of (具有异步功能)迭代您的存储桶

On each iteration, use Promise.all to enqueue 10 calls to download在每次迭代中,使用Promise.all将 10 个调用加入Promise.all以进行download

async function proceed() {
    for await(let bucket of buckets) { // for...of
       await Promise.all(bucket.reduce((accum,fileindex)=>{
           accum.push(download(fileindex)); 
           return accum; 
       },[]));
    }
}

let's see a running example (just 10 buckets, we're all busy here :D )让我们看一个正在运行的示例(只有 10 个存储桶,我们在这里都很忙 :D)

 function download(fileindex) { return new Promise((resolve, reject) => { let file = `file_${fileindex}`; setTimeout(() => { resolve(file); }, 200 + 300 * Math.random()); }); } function* range(bucket, size = 10) { let start = bucket * size, end = start + size; for (let i = start; i < end; i++) { yield i; } } let buckets = [...range(0, 10)].map(bucket => { return [...range(bucket, 10)]; }); async function proceed() { let bucketNumber = 0, timeStart = performance.now(); for await (let bucket of buckets) { let startingTime = Number((performance.now() - timeStart) / 1000).toFixed(1).substr(-5), result = await Promise.all(bucket.reduce((accum, fileindex) => { accum.push(download(fileindex)); return accum; }, [])); console.log( `${startingTime}s downloading bucket ${bucketNumber}` ); await result; let endingTime = Number((performance.now() - timeStart) / 1000).toFixed(1).substr(-5); console.log( `${endingTime}s bucket ${bucketNumber++} complete:`, `[${result[0]} ... ${result.pop()}]` ); } } document.querySelector('#proceed').addEventListener('click',proceed);
 <button id="proceed" >Proceed</button>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM