Async/Await inside Array#map()

Я получаю ошибку времени компиляции с этим кодом:

const someFunction = async (myArray) => {
    return myArray.map(myValue => {
        return {
            id: "my_id",
            myValue: await service.getByValue(myValue);
        }
    });
};

Сообщение об ошибке:

ждать это зарезервированное слово

Почему я не могу использовать это так?

Я также попробовал другой способ, но он дает мне ту же ошибку:

 const someFunction = async (myArray) => {
    return myArray.map(myValue => {
        const myNewValue = await service.getByValue(myValue);
        return {
            id: "my_id",
            myValue: myNewValue 
        }
    });
};

6 ответов

Решение

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

Здесь разумно было бы сделать так, чтобы функция передавалась map асинхронный. Это означает, что map вернул бы массив обещаний. Затем мы можем использовать Promise.all чтобы получить результат, когда все обещания вернутся. Как Promise.all сама возвращает обещание, внешняя функция не должна быть async,

const someFunction = (myArray) => {
    const promises = myArray.map(async (myValue) => {
        return {
            id: "my_id",
            myValue: await service.getByValue(myValue)
        }
    });
    return Promise.all(promises);
}

Если вы хотите запустить карту с функцией асинхронного сопоставления, вы можете использовать следующий код:

const resultArray = await Promise.all(inputArray.map(async (i) => someAsyncFunction(i)));

Как это устроено:

  • inputArray.map(async ...) возвращает массив обещаний - по одному для каждого значения в inputArray.
  • Положив Promise.all() вокруг массива обещаний преобразует его в одно обещание.
  • Единственное обещание от Promise.all() возвращает массив значений - каждый человек обещает разрешить одно значение.
  • Ставим await спереди Promise.all() так что мы ждем, пока объединенное обещание разрешится, и сохраним массив разрешенных вложенных обещаний в переменной resultArray.

В итоге мы получаем одно выходное значение в resultArray для каждого элемента в inputArray, отображаемый через функцию someAsyncFunction. Нам нужно дождаться разрешения всех асинхронных функций, прежде чем станет доступен результат.

Я считаю, что это потому, что функция в map не асинхронный, так что вы не можете ждать в его операторе возврата. Компилируется с этой модификацией:

const someFunction = async (myArray) => {
    return myArray.map(async (myValue) => { // <-- note the `async` on this line
        return {
            id: "my_id",
            myValue: await service.getByValue(myValue)
        }
    });
};

Попробуйте это в Babel REPL

Так что... невозможно дать рекомендацию, не видя остальную часть вашего приложения, но в зависимости от того, что вы пытаетесь сделать, либо сделайте внутреннюю функцию асинхронной, либо попробуйте придумать другую архитектуру для этого блока.

Обновление: мы могли бы получить на высшем уровне ждать один день: https://github.com/MylesBorins/proposal-top-level-await

это будет 2 инструкции, но просто сдвиньте "ожидание" с дополнительной инструкцией

      let results = array.map((e) => fetch('....'))
results  = await Promise.all(results)

Я пробовал все эти ответы, но в моем случае никто не работает, потому что все ответы возвращают promise объект не result of the promise нравится:

      {
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: Array(3)
  0: ...an object data here...
  1: ...an object data here...
  2: ...an object data here...
  length: 3
  [[Prototype]]: Array(0)
}

Затем я нашел этот ответ /questions/56210095/karta-async-await-ne-ozhidaet-zaversheniya-funktsii-async-vnutri-funktsii-kartyi/56210104#56210104 котором указано, не является ли функция асинхронной или обещанной. Поэтому вместо использования внутри map функция, я использую цикл и await отдельный пункт, потому что он сказал, что for петля async осведомлен и приостановит цикл.

Если вы хотите, чтобы каждое переназначенное значение было разрешено перед переходом к следующему, вы можете обработать массив как асинхронную итерацию.

Ниже мы используем библиотеку iter-ops, чтобы преобразовать каждое значение в обещание, а затем создать объект с разрешенным значением, потому что mapсам по себе не должен обрабатывать какие-либо обещания внутри.

      import {pipe, map, wait, toAsync} from 'iter-ops';

const i = pipe(
    toAsync(myArray), // make asynchronous
    map(myValue => {
        return service.getByValue(myValue).then(a => ({id: 'my_id', myValue: a}))
    }),
    wait() // wait for each promise
);

(async function() {
    for await (const a of i) {
        console.log(a); // print resulting objects
    }
})

После каждого значения мы используем ожидание для разрешения каждого переназначенного значения по мере его создания, чтобы требование разрешения соответствовало исходному вопросу.

Другие вопросы по тегам