简体   繁体   中英

async await promises using loops in node

Before I start, this is totally me not fully understanding the complexities of async programming in node / javascript.

I've got a multi dimensional array containing image URLs and output names. I'm trying to iterate through them, download, manipulate and output them using sharp. What I'm trying to do is capture any corrupted images that shape throws as an error, update the array and continue to do stuff with the array when the task has finished.

The problem I'm getting is my code it not waiting for the integrations to finish and therefore not continuing with the updated array.

This is the sort of code I'm using:

let imgArr = [['url1', 'url2', 'url3', 'url4'],['name1', 'name2', 'name3', 'name4']]

function saveImg(url, output)
    return new Promise(async(resolve, reject) => {
       axios.get(url, { responseType: 'arraybuffer' })
       .then((res) => {
          sharp(res.data)
          .resize({changing size etc})
          .png({quality: 95})
          .toFile(output)
          .then(() => { resolve() })
          .catch((err) => { reject() })
    })
}

function processImg(obj){
   return new Promise(async(resolve, reject) => {
      for(let i = 0; i < obj.length; i++){
         saveImg(obj[i][0], obj[i][1])
         .catch((err) => {
             imgArr[i][1] = 'image-not-found.png'
         })
      }
      resolve()
   })
 }

async function doStuff(){
     processImg(imgArr)
        .then(() => {
            console.log(imgArr) // this is where I'd hoped the updated array would appear
        })
        .catch((err) => {
            console.log(err)
        })
)

doStuff()

Thanks.

When you execute promises in a for loop like that, they all start in parallel. Then you call resolve() immediately, before the promises actaully resolve.

You can use Promise.all (or Promise.allSettled as suggested by @funkizer in comments) to fix this:

function processImg(obj){
   return new Promise(async(resolve, reject) => {
      Promise.allSettled(obj.map((item, i) =>
         saveImg(item[0], item[1])
         .catch((err) => {
             imgArr[i][1] = 'image-not-found.png'
         })
      )).then(resolve)
   })
 }

But this wrapping in new Promise is not necessary; you can return the promise directly, as follows:

function processImg(obj){
   return Promise.allSettled(obj.map((item, i) =>
      saveImg(item[0], item[1])
      .catch((err) => {
          imgArr[i][1] = 'image-not-found.png'
      })
   );
}

You can similarly simplify saveImg :

function saveImg(url, output)
    return
       axios.get(url, { responseType: 'arraybuffer' })
       .then((res) =>
          sharp(res.data)
          .resize({changing size etc})
          .png({quality: 95})
          .toFile(output)
       );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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