最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Asyncawait inside for...of vs. map - Stack Overflow

programmeradmin2浏览0评论

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> }

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> }

Share Improve this question edited Jul 11, 2019 at 14:57 mbj asked Jul 11, 2019 at 14:53 mbjmbj 1172 silver badges11 bronze badges 3
  • 3 Your callback returns a promise, so unsurprisingly it maps to promises. – tkausl Commented Jul 11, 2019 at 14:55
  • 2 One way to think about this is that the for loop is a loop inside a single function. That single function returns a single promise. A map() is many function inside a loop. Each function returns a promise so you end up with an array of promises. – Mark Commented Jul 11, 2019 at 14:56
  • This tweet might help you. twitter./ryanflorence/status/1264284487799595008?s=20 – Sagar Commented May 24, 2020 at 2:03
Add a ment  | 

3 Answers 3

Reset to default 4

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

Your first solution waits on every iteration for the promise to resolve, only calling checkLink if the previous one has resolved. This is a sequential solution.

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

The second solution iterates over every element 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 sent out in parallel.

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

const sleep = 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).

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}, ...]
})()

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.

发布评论

评论列表(0)

  1. 暂无评论