Використовуйте функцію async, що чекає, з Array.map


170

Дано наступний код:

var arr = [1,2,3,4,5];

var results: number[] = await arr.map(async (item): Promise<number> => {
        await callAsynchronousOperation(item);
        return item + 1;
    });

яка створює таку помилку:

TS2322: Введіть 'Обіцяти <номер> []' не можна присвоїти типу 'номер []'. Тип "Обіцяння <номер> не призначається типу" номер ".

Як я можу це виправити? Як я можу скласти async awaitта Array.mapпрацювати разом?


6
Чому ви намагаєтеся зробити синхронну операцію в операцію асинхронізації? arr.map()є синхронним і не повертає обіцянки.
jfriend00

2
Ви не можете надіслати асинхронну операцію функції, наприклад map, яка очікує синхронної, і очікувати, що вона працює.
Єретична мавпа

1
@ jfriend00 У мене багато функцій очікування на внутрішній функції. Це насправді довга функція, і я просто спростив її, щоб зробити її читабельною. Тепер я додав дзвінок, що очікує, щоб зрозуміти, чому це має бути асинхронізація.
Алон

Вам потрібно чекати чогось, що повертає обіцянку, а не те, що повертає масив.
jfriend00

2
Одне корисне, що потрібно усвідомити - це те, що кожен раз, коли ви позначаєте функцію як async,, ви робите цю функцію. Тож звичайно, карта асинхрону повертає масив обіцянок :)
Ентоні Меннінг-Франклін

Відповіді:


380

Проблема тут полягає в тому, що ви намагаєтесь awaitотримати масив обіцянок, а не обіцянок. Це не робить те, що ви очікуєте.

Коли об'єкт, переданий об'єкту await, не є Обіцянням, awaitпросто повертає значення як - є, а не намагається вирішити його. Отже, оскільки ви передали awaitмасив (об'єктів Promise) сюди замість Promise, значення, яке повертається в очікуванні, - це просто той масив, який має тип Promise<number>[].

Що вам потрібно зробити тут, це зателефонувати Promise.allна масив, повернутий mapдля того, щоб перетворити його в єдину Обіцянку перед тим, як подати awaitйого.

Відповідно до документів MDN дляPromise.all :

Promise.all(iterable)Метод повертає обіцянку , яке вирішує , коли всі обіцянки в ітерації аргументу вирішив, чи покидьки з причиною першого минулим обіцянки, отвергающими.

Тож у вашому випадку:

var arr = [1, 2, 3, 4, 5];

var results: number[] = await Promise.all(arr.map(async (item): Promise<number> => {
    await callAsynchronousOperation(item);
    return item + 1;
}));

Це дозволить вирішити конкретну помилку, з якою ви стикаєтесь тут.



11
@DanielPendergast Це для анотацій типу в TypeScript.
Ajedi32

Яка різниця між дзвінками callAsynchronousOperation(item);з або без awaitфункцій карти асинхронізації?
nerdizzle

@nerdizzle Це звучить як хороший кандидат для іншого питання. В основному, awaitфункція буде чекати, коли асинхронна операція завершиться (або вийде з ладу) перед продовженням, інакше вона просто негайно продовжиться, не чекаючи.
Ajedi32

@ Ajedi32 thx для відповіді. Але без очікування на карті асинхронізації вже не можна чекати повторного результату функції?
nerdizzle

15

Є ще одне рішення для цього, якщо ви використовуєте не рідні обіцянки, а Bluebird.

Ви також можете спробувати використовувати Promise.map () , змішавши array.map та Promise.all

У вашому випадку:

  var arr = [1,2,3,4,5];

  var results: number[] = await Promise.map(arr, async (item): Promise<number> => {
    await callAsynchronousOperation(item);
    return item + 1;
  });

2
Він інший - він не виконує всі операції паралельно, а швидше виконує їх послідовно.
Андрій Церкус

5
@AndreyTserkus Promise.mapSeriesабо Promise.eachє послідовним, Promise.mapзапускає їх все відразу.
Kiechlus

1
@AndreyTserkus ви можете виконувати всі або деякі операції паралельно, надаючи concurrencyопцію.

11
Варто згадати, що це не ванільний JS.
Міхал

@Michal Так, це SyntaxError
CS QGB


2

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

const arr = [1,2,3,4,5];
let resultingArr = [];
for (let i in arr){
  await callAsynchronousOperation(i);
  resultingArr.push(i + 1)
}

6
Promise.all буде асинхронізована для кожного елемента масиву. Це буде синхронізація, доведеться чекати, коли закінчить один елемент, щоб розпочати наступний.
Сантьяго Мендоса Рамірес

Для тих, хто намагається використовувати цей підхід, зауважте, що для..of є правильним способом ітерації вмісту масиву, тоді як для .. в ітерації над індексами.
ralfoide

2

Рішення нижче, щоб асинхронно обробити всі елементи масиву І зберегти порядок:

const arr = [1, 2, 3, 4, 5, 6, 7, 8];
const randomDelay = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1000));

const calc = async n => {
  await randomDelay();
  return n * 2;
};

const asyncFunc = async () => {
  const unresolvedPromises = arr.map(n => calc(n));
  const results = await Promise.all(unresolvedPromises);
};

asyncFunc();

Також кодек .

Зверніть увагу, що ми лише "чекаємо" на Promise.all. Ми називаємо calc без "очікування" кілька разів, і ми збираємо масив невирішених обіцянок відразу. Тоді Promise.all чекає вирішення всіх з них і повертає масив із вирішеними значеннями в порядку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.