forEach/map dan async tidak berjalan seiring

May 07 2023
Kadang-kadang, kita cenderung melewatkan fungsi async dengan fungsi forEach dan peta. Misalnya: Hasil: Semua angka dari 1.
Tajuk

Kadang-kadang, kita cenderung melewatkan fungsi async dengan fungsi forEach dan peta.

Misalnya:

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

Hasil: Semua angka dari 1..5 dicetak satu demi satu tetapi tidak ada jeda 1 detik setelah mencetak setiap baris.

Mengapa ini terjadi?

Lihatlah fungsi javascipt ini:

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

Hasil: Ini sebenarnya mengembalikan Promise yang menghasilkan angka.

Jika kita menulis TypeScript yang setara dengan fungsi ini dengan tipe yang ketat, itu akan memperjelas semuanya.

Kode berikut tidak dapat dikompilasi:

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

Versi yang benar adalah:

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

Jangan tertipu oleh gula sintaksis yang disediakan oleh async-await. Jika kita menulis janji murni tanpa menggunakan async, fungsi di atas akan terlihat seperti:

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

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

fungsi async mengembalikan janji dan bukan nilai
  1. Kami memiliki doubleOffungsi yang menerima angka dan mengembalikan angka. Javascript lama biasa.
  2. Kami memiliki doubleOfOldWayfungsi yang menerima angka dan mengembalikan janji yang diselesaikan menjadi angka.
  3. Kami memiliki doubleOfNewWay, fungsi async yang menerima angka dan sepertinya mengembalikan angka, tetapi sebenarnya mengembalikan janji yang menyelesaikan ke angka seperti doubleOfOldWayfungsi.
  4. doubleOfOldWaydan doubleOfNewWayfungsinya persis sama.
  5. Dan karenanya, ketika kami mencoba untuk menjalankan operasi penggandaan pada nilai yang dikembalikan oleh doubleOfOldWaydan doubleOfNewWayfungsi, hasilnya adalah NaN, karena kami tidak dapat menjanjikan banyak (jelas!).
  6. untuk mengalikan doubleOfOldWaydan doubleOfNewWay:

Jadi kembali ke contoh awal kita:

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

Cara yang paling tepat untuk mengimplementasikan apa yang kita harapkan dari fungsi forEach ini adalah dengan menggunakan loop for sederhana:

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}

Untuk mendapatkan daftar ganda dari setiap angka, yang bisa kita lakukan adalah:

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

[2, 4, 6, 8, 10]