Async / Await inside Reduce - PullRequest
       38

Async / Await inside Reduce

1 голос
/ 08 января 2020

Я пытаюсь использовать async / await с методом reduce. Но почему-то работает только первая итерация l oop. Объект бумаг пропускает await после первой итерации reduce. Можно ли создать объект, используя reduce, собирая его содержимое из обещания, а также ожидая всего процесса?

Функция уменьшения:

const papers = await page.concepts.reduce(async (acc, cur) => {
  acc[cur.paperID] = await getPageData(`papers/${ cur.paperID }`, 'md')
  return acc
}, {})

Примечание: getPageData возвращает обещание.

Ответы [ 3 ]

2 голосов
/ 08 января 2020

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

const papersPromises = page.concepts.map(cur => 
    getPageData(`papers/${ cur.paperID }`, 'md')
)

const result = await Promise.all(papersPromises)
// result will be an array with the results
1 голос
/ 08 января 2020

Поскольку функция asyn c возвращает обещание, вам следует ожидать, что ваш аккумулятор - это обещание. Таким образом, вы должны сделать что-то вроде этого:

console.log("Program starting at", Date());

function delayedResolver(a, ms) {
    return new Promise(function(resolve, reject) {
        setTimeout(() => {
            console.log("resolving at", Date());
            resolve(a);
        }, ms);
    });
}

arr = [1, 3, 5];

r = arr.reduce(async function(a, b) { 
      return await delayedResolver(a, 3000).then(v => v + b); 
    }, Promise.resolve(0));

console.log(r);

r.then(v => console.log("final value", v, "at", Date()));

Вы должны рассматривать аккумулятор как обещание и каждый раз возвращать новое обещание.

Обратите внимание, что функция asyn c возвращает обещание и reduce немедленно применяют функцию asyn c к следующей паре (accumulator, entry), поэтому все эти обещания, возвращаемые функцией asyn c, создаются почти мгновенно, поэтому обещания разрешаются все после 3 секунд, а не по одной.

1 голос
/ 08 января 2020

Это один из способов сложить массив в объект, но, естественно, он не будет обрабатывать функции asyn c и сокращать себя. Кроме того, сокращение не возвращает обещание, ожидаемое для

. Вы можете решить свое дело с помощью Promise.all и Object.fromEntries

// map concepts to promises which will resolve to page data for each concept
// and await for them all at once (will run in parallel)
const pageResponses = await Promise.all(page.concepts.map(concept => getPageData(`papers/${concept.paperID}`, 'md')))
// construct object where keys are concept.paperIDs and values are pageDatas
const papers = Object.fromEntries(
  page.concepts
    .map((concept, i) => ([concept.paperID, pageResponses[i]]))
)

если ваша среда выполнения не поддерживает Object.fromEntries (и вы не используете polyfill), вы можете выполнить последнюю часть следующим образом

const papers = page.concepts.reduce((acc, concept, index) => acc[concept.paperID] = pageResponses[index], {})
...