JavaScript-массив .reduce with async/wait

Похоже, что у вас есть некоторые проблемы, включающие async/wait с .reduce(), например:

const data = await bodies.reduce(async(accum, current, index) => {
  const methodName = methods[index]
  const method = this[methodName]
  if (methodName == 'foo') {
    current.cover = await this.store(current.cover, id)
    console.log(current)
    return {
      ...accum,
      ...current
    }
  }
  return {
    ...accum,
    ...method(current.data)
  }
}, {})
console.log(data)

Объект data регистрируется до завершения this.store...

Я знаю, что вы можете использовать Promise.all с асинхронными циклами, но относится ли это к .reduce()?

Ответ 1

Проблема в том, что ваши значения аккумулятора promises - это возвращаемые значения async function s. Чтобы получить последовательную оценку (и все, кроме последней итерации, которую вы ожидаете вообще), вам нужно использовать

const data = await array.reduce(async (accumP, current, index) => {
  const accum = await accumP;
  …
}, Promise.resolve(…));

Тем не менее, для async/await я бы рекомендовал использовать простые циклы вместо методов итерации массива, они более эффективны и часто проще.

Ответ 2

Мне нравится ответ Берги, я думаю, что это правильный путь.

Я также хотел бы упомянуть мою библиотеку под названием Awaity.js

Это позволяет вам без особых усилий использовать такие функции, как reduce, map и filter с помощью async/await:

import reduce from 'awaity/reduce';

const posts = await reduce([1,2,3], async (posts, id) => {

  const res = await fetch('/api/posts/' + id);
  const post = await res.json();

  return {
    ...posts,
    [id]: post
  };
}, {})

posts // { 1: { ... }, 2: { ... }, 3: { ... } }

Ответ 3

Вы можете обернуть всю свою карту/уменьшить блоки итератора в свой собственный Promise.resolve и ждать завершения этого. Однако проблема заключается в том, что аккумулятор не содержит результирующих данных/объектов, которые вы ожидаете на каждой итерации. Из-за внутренней цепочки асинхронных/ожидающих/обещаний, аккумулятор будет фактическим Promises самим, который, вероятно, еще предстоит решить самостоятельно, несмотря на использование ключевого слова ожидания до вашего вызова в магазин (что может заставить вас поверить, что итерация выиграла " t до тех пор, пока этот вызов не завершится, и аккумулятор не будет обновлен.

Хотя это не самое элегантное решение, вам нужно переместить объектную переменную данных вне области действия и назначить ее как let, чтобы правильная привязка и может произойти мутация. Затем обновите этот объект данных изнутри вашего итератора при разрешении async/await/Promise.

/* allow the result object to be initialized outside of scope 
   rather than trying to spread results into your accumulator on iterations, 
   else your results will not be maintained as expected within the 
   internal async/await/Promise chain.
*/    
let data = {}; 

await Promise.resolve(bodies.reduce(async(accum, current, index) => {
  const methodName = methods[index]
  const method = this[methodName];
  if (methodName == 'foo') {
    // note: this extra Promise.resolve may not be entirely necessary
    const cover = await Promise.resolve(this.store(current.cover, id));
    current.cover = cover;
    console.log(current);
    data = {
      ...data,
      ...current,
    };
    return data;
  }
  data = {
    ...data,
    ...method(current.data)
  };
  return data;
}, {});
console.log(data);

Ответ 4

export const addMultiTextData = async(data) => {
  const textData = await data.reduce(async(a, {
    currentObject,
    selectedValue
  }) => {
    const {
      error,
      errorMessage
    } = await validate(selectedValue, currentObject);
    return {
      ...await a,
      [currentObject.id]: {
        text: selectedValue,
        error,
        errorMessage
      }
    };
  }, {});
};