forEach/map dan async tidak berjalan seiring
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));
}
- Kami memiliki
doubleOf
fungsi yang menerima angka dan mengembalikan angka. Javascript lama biasa. - Kami memiliki
doubleOfOldWay
fungsi yang menerima angka dan mengembalikan janji yang diselesaikan menjadi angka. - Kami memiliki
doubleOfNewWay
, fungsi async yang menerima angka dan sepertinya mengembalikan angka, tetapi sebenarnya mengembalikan janji yang menyelesaikan ke angka sepertidoubleOfOldWay
fungsi. doubleOfOldWay
dandoubleOfNewWay
fungsinya persis sama.- Dan karenanya, ketika kami mencoba untuk menjalankan operasi penggandaan pada nilai yang dikembalikan oleh
doubleOfOldWay
dandoubleOfNewWay
fungsi, hasilnya adalahNaN
, karena kami tidak dapat menjanjikan banyak (jelas!). - untuk mengalikan
doubleOfOldWay
dandoubleOfNewWay
:
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]