Використовуйте функцію 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.