291 votes

Utiliser async await avec Array.map

Étant donné le code suivant :

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

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

ce qui produit l'erreur suivante :

TS2322 : Le type 'Promise<number>[]' n'est pas assignable au type 'number[]'. Le type 'Promise<number> n'est pas assignable au type 'number'.

Comment puis-je le réparer ? Comment puis-je faire async await et Array.map travailler ensemble ?

6 votes

Pourquoi essayez-vous de transformer une opération synchrone en une opération asynchrone ? arr.map() est synchrone et ne renvoie pas de promesse.

2 votes

Vous ne pouvez pas envoyer une opération asynchrone à une fonction, comme map qui s'attend à ce qu'il soit synchrone, et s'attendre à ce que cela fonctionne.

2 votes

@jfriend00 J'ai plusieurs instructions await dans la fonction interne. C'est en fait une longue fonction et je l'ai juste simplifiée pour la rendre lisible. J'ai ajouté maintenant un appel await pour rendre plus claire la raison pour laquelle elle doit être asynchrone.

636voto

Ajedi32 Points 5367

Le problème ici est que vous essayez de await un tableau de promesses plutôt qu'une promesse. Cela ne fait pas ce que vous attendez.

Lorsque l'objet passé à await n'est pas une promesse, await renvoie simplement la valeur telle quelle immédiatement au lieu d'essayer de la résoudre. Donc, puisque vous avez passé await un tableau (d'objets Promise) ici au lieu d'une Promise, la valeur renvoyée par await est simplement ce tableau, qui est de type Promise<number>[] .

Ce que vous devez faire ici, c'est appeler Promise.all sur le tableau retourné par map afin de le convertir en une seule Promesse avant que await l'ingérer.

Selon le Documents MDN pour Promise.all :

Le site Promise.all(iterable) renvoie une promesse qui résout lorsque toutes les promesses de l'argument itérable ont été résolues, ou bien rejette avec la raison de la première promesse passée qui rejette.

Donc dans votre cas :

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;
}));

Cela permettra de résoudre l'erreur spécifique que vous rencontrez ici.

1 votes

Que font les : Les deux points signifient ?

17 votes

@DanielPendergast C'est pour les annotations de type dans TypeScript.

0 votes

Quelle est la différence entre appeler callAsynchronousOperation(item); avec et sans await à l'intérieur de la fonction asynchrone Map ?

20voto

Gabriel Cheung Points 312

Il existe une autre solution pour cela si vous n'utilisez pas les Promesses natives mais Bluebird.

Vous pouvez également essayer d'utiliser Promise.map() en mélangeant array.map et Promise.all

Dans votre cas :

  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 votes

Il est différent - il n'exécute pas toutes les opérations en parallèle, mais les exécute plutôt en séquence.

5 votes

@AndreyTserkus Promise.mapSeries ou Promise.each sont séquentielles, Promise.map les lance tous en même temps.

1 votes

@AndreyTserkus vous pouvez exécuter toutes ou certaines opérations en parallèle en fournissant concurrency option.

15voto

Miki Points 108

Solution ci-dessous pour traiter tous les éléments du tableau en parallèle, de manière asynchrone ET préserver l'ordre :

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();

Aussi codepen .

Remarquez que nous ne faisons "await" que pour Promise.all. Nous appelons calc sans "await" plusieurs fois, et nous collectons un tableau de promesses non résolues immédiatement. Ensuite, Promise.all attend la résolution de toutes ces promesses et renvoie un tableau avec les valeurs résolues dans l'ordre.

12voto

Dan Beaulieu Points 160

Si vous établissez une correspondance avec un tableau de promesses, vous pouvez ensuite les résoudre toutes en un tableau de nombres. Voir Promise.all .

5voto

Beckster Points 23

Je vous recommande d'utiliser Promise.all comme indiqué ci-dessus, mais si vous voulez vraiment éviter cette approche, vous pouvez utiliser une boucle for ou toute autre boucle :

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

FYI : Si vous voulez itérer sur les éléments d'un tableau, plutôt que sur les indices (commentaire de @ralfoide), utilisez of au lieu de in à l'intérieur de let i in arr déclaration.

9 votes

La Promise.all sera asynchrone pour chaque élément du tableau. Ce sera une synchro, il faudra attendre de finir un élément pour commencer le suivant.

1 votes

Pour ceux qui essaient cette approche, notez que for..of est la façon correcte d'itérer le contenu d'un tableau, alors que for..in itère sur les indices.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X