forEach/map i async nie idą w parze

May 07 2023
Czasami często przekazujemy funkcje asynchroniczne z funkcjami forEach i map. Na przykład: Wynik: Wszystkie liczby od 1.
nagłówek

Czasami często przekazujemy funkcje asynchroniczne z funkcjami forEach i map.

na przykład:

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

Wynik: Wszystkie liczby od 1 do 5 są drukowane jedna po drugiej, alepo wydrukowaniu każdej linii nie ma 1-sekundowej przerwy .

Dlaczego to się dzieje?

Spójrz na tę funkcję javascipt:

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

Wynik: W rzeczywistości jest to zwrócenie Obietnicy, która rozwiązuje się na liczbę.

Jeśli napiszemy odpowiednik tej funkcji w maszynopisie z typami ścisłymi, wszystko stanie się jasne.

Poniższy kod nie skompiluje się:

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

Poprawna wersja to:

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

Nie daj się nabrać na syntaktyczny cukier dostarczany przez async-await. Gdybyśmy napisali czyste obietnice bez użycia asynchronizacji, powyższa funkcja wyglądałaby następująco:

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

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

funkcje asynchroniczne zwracają obietnice, a nie wartości
  1. Mamy doubleOffunkcję, która pobiera liczbę i zwraca liczbę. Zwykły stary javascript.
  2. Mamy doubleOfOldWayfunkcję, która przyjmuje liczbę i zwraca obietnicę, która rozwiązuje się na liczbę.
  3. Mamy doubleOfNewWayfunkcję asynchroniczną, która przyjmuje liczbę i wygląda na to, że zwraca liczbę, ale w rzeczywistości zwraca obietnicę, która rozwiązuje się na liczbę, tak jak doubleOfOldWayfunkcja.
  4. doubleOfOldWayi doubleOfNewWayfunkcje są dokładnie takie same.
  5. I stąd, gdy próbujemy wykonać operację mnożenia na wartościach zwracanych przez funkcje doubleOfOldWayi doubleOfNewWay, wynikiem jest NaN, ponieważ nie możemy składać wielu obietnic (oczywiście!).
  6. Aby pomnożyć doubleOfOldWayi doubleOfNewWay:

Wróćmy więc do naszego początkowego przykładu:

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

Najbardziej poprawnym sposobem zaimplementowania tego, czego oczekujemy od tej funkcji forEach, jest użycie prostej pętli 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}

Aby uzyskać listę podwójnych wartości każdej liczby, możemy:

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

[2, 4, 6, 8, 10]