Async/await inside for…of vs. map

∥☆過路亽.° 提交于 2020-12-12 10:31:30

问题


I'm trying to figure out why promises seem to work differently inside of for...of vs. map().

data is an array of objects of the form {app: MY_APP, link: MY_LINK}. I'm trying to convert it to the form {app: MY_APP, status: true OR false}}. The function checkLink() is async because it uses node-fetch and basically returns whether the given link is 200. Using for...of, I get the desired object:

let objToSend = [];
for (obj of data) {
  objToSend.push({ app: obj.app, status: await checkLink(obj.link) });
}

// returns [{app: MY_APP, status: true}, ...]

But using map, as follows, I get an array of pending promises instead. Any help would be appreciated:

let objToSend = data.map(async obj => {
  return {
    app: obj.app,
    status: await checkLink(obj.link)
  };
});

// returns [ Promise { <pending> }, ...]

I also tried doing Promise.all(objToSend) after the map code, but it instead returns Promise { <pending> }


回答1:


I'll leave the explanation up to the other answers, but just want to point out that there is also an performance difference.

You first solution waits on every iteration for the promise to resolve. Only calling checkLink if the previous one has resolved. This is an sequential solution.

check link1 => wait => check link2 => wait => check link3 => wait

The second solution iterates over every elements and sends out requests, without waiting for promises to resolve (for this reason an array of promises is returned). If you wait for all promises to be resolved you find this solution is a lot faster, since the requests are send out in parallel.

check link1 => check link2 => check link3 => wait for all

const sleep = async ms => new Promise(resolve => setTimeout(resolve, ms));

async function checkLink(link) {
  await sleep(Math.random() * 1000 + 500); // sleep between 500 and 1500 ms
  console.log(link);
  return `status #${link}`;
}

(async function () {
  const data = new Array(5).fill().map((_, index) => ({ app: "app", link: index }));
  let objToSend;
  
  console.log("solution #1");
  objToSend = [];
  for (let obj of data) {
    objToSend.push({ app: obj.app, status: await checkLink(obj.link) });
  }
  console.log(objToSend);

  console.log("==============================================");
  
  console.log("solution #2");
  objToSend = await Promise.all(data.map(async obj => {
    return {
      app: obj.app,
      status: await checkLink(obj.link)
    };
  }));
  console.log(objToSend);
})();

In the snippet the first solution takes 500/1500 * 5 = 2500/7500 between 2500 and 7500 ms. While the second solution takes between 500 and 1500 ms (depending on the slowest value to resolve).




回答2:


The await always halts the execution of the async function it is in, it doesn't work differently in both cases. In the later example however, you do call some async functions, and those calls will evaluate to promises, whereas in your first example, all those awaits are in the same async function.

I also tried doing Promise.all(objToSend) after the map code, but it instead returns Promise { <pending> }

Yup, await that and you get an array of results.




回答3:


Async functions always returns Promises. In your map function, as the callback returns promises, an array of promises is being created.

To convert it to your format, use it with Promise.all():

(async()=>{
    let objToSend = await Promise.all(data.map(async obj => {
        return {
            app: obj.app,
            status: await checkLink(obj.link)
        };
    }));
    console.log(objToSend) //[{app: MY_APP, status: true}, ...]
})()


来源:https://stackoverflow.com/questions/56992054/async-await-inside-for-of-vs-map

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!