Асинхронизация / ожидание внутри для ... of vs. map - PullRequest
1 голос
/ 11 июля 2019

Я пытаюсь выяснить, почему обещания работают по-разному внутри for...of против map().

data - это массив объектов вида {app: MY_APP, link: MY_LINK}.Я пытаюсь преобразовать его в форму {app: MY_APP, status: true OR false}}.Функция checkLink() является асинхронной, потому что она использует node-fetch и в основном возвращает, является ли данная ссылка 200. Используя for...of, я получаю нужный объект:

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

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

Но используя map, так какЗатем я получаю массив ожидающих обещаний.Любая помощь приветствуется:

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

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

Я также пытался сделать Promise.all(objToSend) после кода map, но вместо этого он возвращает Promise { <pending> }

Ответы [ 3 ]

1 голос
/ 11 июля 2019

Асинхронные функции всегда возвращает обещания .В вашей функции карты, поскольку обратный вызов возвращает обещания, создается массив обещаний.

Чтобы преобразовать его в ваш формат, используйте его с 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}, ...]
})()
1 голос
/ 11 июля 2019

Я оставлю объяснение вплоть до других ответов, но просто хочу указать, что есть и разница в производительности.

Первое решение ждет на каждой итерации обещание, которое нужно выполнить.Вызывать checkLink можно только в том случае, если предыдущий разрешен.Это последовательное решение.

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

Второе решение перебирает все элементы и отправляет запросы, не дожидаясь разрешения обещаний (по этой причине возвращается массив обещаний).Если вы дождетесь выполнения всех обещаний, то обнаружите, что это решение намного быстрее, поскольку запросы отправляются параллельно.

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);
})();

Во фрагменте первое решение занимает 500/1500 * 5 = 2500/7500 между 2500 и 7500 мс.Второе решение занимает от 500 до 1500 мс (в зависимости от самого медленного значения для разрешения).

1 голос
/ 11 июля 2019

await всегда останавливает выполнение async function, в котором он находится, он не работает по-разному в обоих случаях. В последнем примере, однако, вы вызываете несколько async function с, и эти вызовы будут преобразованы в обещания, тогда как в вашем первом примере все эти await s находятся в одном и том же async function.

Я также пытался сделать Promise.all(objToSend) после кода карты, но вместо этого он возвращает Promise { <pending> }

Да, await это и вы получите массив результатов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...