首页 > 解决方案 > 等待异步函数和其中的承诺完成

问题描述

我的任务:我有一个包含许多项目的文件,每个项目都与我需要下载的图像 URL 数组相关。我想下载所有链接,我正在使用这个库来下载图像,并且我正在使用 Promise。

问题: 当我开始从许多项目下载许多图像时出现问题,程序在第一个完成之前发送了 4000 多个请求并且程序崩溃了。

我的解决方案:我的想法是一次只处理大约 2 个项目,以便我一次下载大约 20 个图像。我已经尝试过各种带有 promises 和 async 函数的变体,但我对这些很陌生,所以我的尝试失败了。

我的代码流程是这样的:

csvRun()

function csvRun(){
    for(let i = 1; i <= itemsAmount; i++){  // Loops over the items
        // I want to be able to run only x items at a time
        console.log('Item number ' + i)
        itemHandle() 
    }
}

function itemHandle(){ // This function seems useless here but since the item has more data I kept it here
    handleImages()
}


function handleImages(){  // Loops over the images of the item
    for(let g = 0; g < imagesAmount; g++){        
        // Here there is a promise that downloads images
        // For the example I'll use settimout
        setTimeout(() => {
            console.log('Image downloaded ' + g)
        }, 3000);

        /** If you want the error just use ImgDonwload instead of
            settimeout and set imagesAmount to 20 and itemsAmount 
            to 400
        */ 

    }

}

// Only here to recreate the error. Not necessarily relevant.
function ImgDownload(){
    var download = require('image-downloader')
    download // returns the promise so the handling could resume in order
    .image({
        url:
            "https://cdn.vox-cdn.com/thumbor/XKPu8Ylce2Cq6yi_pgyLyw80vb4=/0x0:1920x1080/1200x800/filters:focal(807x387:1113x693)/cdn.vox-cdn.com/uploads/chorus_image/image/63380914/PIA16695_large.0.jpg",
        dest: "/folder/img.jpg"
    })
    .then(({ filename, image }) => {
        console.log("File saved to", filename);
    })
    .catch((err: Error) => {
        console.error(err);
    });
}

目前,代码完成循环csvRun并在 3 秒Item number 1Item number {itemsAmount}打印出所有Image downloaded messages. 我明白为什么会这样。我想更改代码,以便每次只itemHandle同时进行 2 个调用。

标签: javascriptnode.jsasynchronous

解决方案


一种选择是有一个循环遍历图像并一个接一个地处理。然后并行运行多个处理,启动多个循环:

  // Goes over the "data" array, calls and waits for each "task" and processes "runnerCount" tasks in parallel
  function inParallel(task, data, runnerCount) {
    let i = 0, results = [];

    async function runner() {
      while(i < data.length) {
         const pos = i++; // be aware: concurrent modification of i
         const entry = data[pos]; 
         results[pos] = await task(entry);
      }
   }

    const runners = Array.from({ length: runnerCount }, runner);

    return Promise.all(runners).then(() => results);
 }

用作:

  const delay = ms => new Promise(res => setTimeout(res, ms));

 inParallel(async time => {
   console.log(`Timer for ${time}ms starts`);
   await delay(time);
   console.log(`Timer for ${time}ms ends`);
 }, [5000, 6000, 1000]/*ms*/, 2/*in parallel*/);

推荐阅读