forEach/map e async non vanno di pari passo

May 07 2023
A volte, tendiamo spesso a passare funzioni asincrone con funzioni forEach e map. Ad esempio: Risultato: tutti i numeri da 1.
Intestazione

A volte, tendiamo spesso a passare funzioni asincrone con funzioni forEach e map.

Per es:

[1,2,3,4,5].forEach(async (n) => setTimeout(console.log(n), 1000));

Risultato: tutti i numeri da 1 a 5 vengono stampati uno dopo l'altro ma non c'è intervallo di 1 secondo dopo la stampa di ciascuna riga.

Perché succede?

Guarda questa funzione javascript:

async function doubleOf(n) {
  return n*2;
}

Risultato: questo in realtà restituisce una Promessa che si risolve in un numero.

Se scriviamo un equivalente Typescript di questa funzione con tipi rigorosi, chiarirà le cose.

Il seguente codice non verrà compilato:

async function doubleOf(n: number): number {
  return n*2;
}

La versione corretta sarebbe:

async function doubleOf(n: number): Promise<number> {
  return n*2;
}

Non lasciarti ingannare dallo zucchero sintattico fornito da async-await. Se scrivessimo promesse pure senza usare async, la funzione sopra sarebbe simile a:

function doubleOf(n) {
  return new Promise((resolve) => resolve(n*2));
}

function doubleOf(n: number): Promise<number> {
  return new Promise((resolve) => resolve(n*2));
}

le funzioni asincrone restituiscono promesse e non valori
  1. Abbiamo doubleOfuna funzione che accetta un numero e restituisce un numero. Semplice vecchio javascript.
  2. Abbiamo doubleOfOldWayuna funzione che accetta un numero e restituisce una promessa che si risolve in un numero.
  3. Abbiamo doubleOfNewWay, una funzione asincrona che accetta un numero e sembra che restituisca un numero, ma in realtà restituisce una promessa che si risolve in un numero proprio come la doubleOfOldWayfunzione.
  4. doubleOfOldWaye doubleOfNewWayle funzioni sono esattamente le stesse.
  5. E quindi, quando proviamo ad eseguire un'operazione di moltiplicazione sui valori restituiti da doubleOfOldWaye doubleOfNewWayfunzioni, il risultato è stato NaN, poiché non possiamo più promesse (ovviamente!).
  6. Moltiplicare doubleOfOldWaye doubleOfNewWay:

Quindi torniamo al nostro esempio iniziale:

[1,2,3,4,5].forEach(async (n) => setTimeout(console.log(n), 1000));

Il modo più corretto per implementare ciò che ci aspettiamo da questa funzione forEach è usare un semplice ciclo for:

for(const number of [1,2,3,4,5]) {
    console.log(number);
    await new Promise(resolve => setTimeout(resolve, 1000)); // Sleep for "atleast" 1 second
}

[1,2,3,4,5].map(async (n) => n*2);

(5) [Promise, Promise, Promise, Promise, Promise]
0: Promise {<fulfilled>: 2}
1: Promise {<fulfilled>: 4}
2: Promise {<fulfilled>: 6}
3: Promise {<fulfilled>: 8}
4: Promise {<fulfilled>: 10}

Per ottenere un elenco di doppio di ogni numero, quello che possiamo fare è:

await Promise.all([1,2,3,4,5].map(async (n) => n*2));

[2, 4, 6, 8, 10]