- Bab 11 dari 12
- 35 menit
- 1 latihan kode
- 4 soal kuis
Promise, Generator, dan Struktur Data
Di bab ini (9 bagian)
Tujuan bab ini
- Memahami kenapa JavaScript butuh kode asynchronous dan apa itu callback hell.
- Membuat dan merangkai Promise, serta memilih antara
Promise.all,allSettled,race, danany. - Membuat iterator sendiri dengan generator (
function*danyield). - Memakai
Map,Set,Symbol, danfor...of, plus mengenal WeakMap, WeakSet, Proxy, dan Reflect.
Kenapa harus asynchronous?
JavaScript berjalan di satu thread. Bayangkan kasir tunggal di kafe. Kalau kasir menunggu kopi selesai diseduh sebelum melayani pembeli berikutnya, antrean macet. Kasir yang pintar mencatat pesanan, memberi nomor antrean, lalu lanjut melayani orang lain. Saat kopi siap, nomormu dipanggil.
Operasi lambat seperti mengambil data dari server atau membaca file diperlakukan sama: JavaScript menitipkannya, lanjut mengerjakan kode lain, lalu kembali saat hasilnya siap.
Walau jedanya 0 ms, B tetap muncul terakhir karena callback timer baru dijalankan setelah kode yang sedang berjalan selesai.
Cara lama menangani hasil asynchronous adalah callback: fungsi yang dititipkan untuk dipanggil nanti. Masalahnya muncul kalau langkahnya berurutan.
Bentuk piramida ini dijuluki callback hell: susah dibaca, dan penanganan error harus ditulis ulang di setiap tingkat.
Promise: janji yang pasti ditepati atau dibatalkan
Promise (ES2015) adalah objek yang mewakili hasil yang belum ada sekarang. Ia punya tiga state:
| State | Arti |
|---|---|
| pending | masih berjalan, belum ada hasil |
| fulfilled | berhasil, membawa sebuah nilai |
| rejected | gagal, membawa alasan (biasanya Error) |
Begitu fulfilled atau rejected, state-nya terkunci dan tidak bisa berubah lagi.
.then(fn)dipanggil saat fulfilled..catch(fn)dipanggil saat rejected, termasuk kalau ada error dilempar di.thensebelumnya..finally(fn)(ES2018) selalu jalan, cocok untuk menyembunyikan loading spinner.
Chaining
Kuncinya: .then() selalu mengembalikan Promise baru. Nilai yang kamu return di dalamnya menjadi input .then() berikutnya. Kalau yang di-return adalah Promise, rantai menunggu Promise itu selesai dulu.
Piramida callback tadi jadi rata:
Menjalankan banyak Promise sekaligus
| Method | Selesai saat | Gagal saat |
|---|---|---|
Promise.all (ES2015) | semua fulfilled, hasilnya array | salah satu rejected |
Promise.allSettled (ES2020) | semua selesai, apa pun hasilnya | tidak pernah reject |
Promise.race (ES2015) | yang pertama selesai, berhasil atau gagal | yang pertama ternyata rejected |
Promise.any (ES2021) | yang pertama fulfilled | semua rejected (AggregateError) |
Tiap item hasil allSettled berbentuk { status: "fulfilled", value } atau { status: "rejected", reason }.
Iterator dan generator
Iterator adalah objek dengan method next() yang mengembalikan { value, done }. Objek yang bisa memberikan iterator lewat [Symbol.iterator]() disebut iterable: array, string, Map, dan Set termasuk.
Menulis iterator manual itu repot. Generator (function*) membuatnya gampang. Setiap yield menyerahkan satu nilai lalu menjeda fungsi sampai next() berikutnya dipanggil.
Karena nilainya dibuat sesuai permintaan (lazy), generator bahkan boleh tak terbatas, asal kamu berhenti mengambil di suatu titik.
Map dan Set
Map menyimpan pasangan key-value seperti objek, tapi key-nya boleh tipe apa saja, urutannya terjaga, dan ukurannya langsung ada di .size.
Set menyimpan nilai unik. Trik paling populer: membuang duplikat array.
WeakMap dan WeakSet adalah versi "lemah": key-nya harus objek dan tidak menahan objek itu dari garbage collector. Begitu objek tidak dipakai di tempat lain, entrinya ikut hilang. Konsekuensinya, keduanya tidak punya .size dan tidak bisa di-loop. Cocok untuk menempelkan data tambahan ke objek, misalnya elemen DOM, tanpa risiko memory leak.
Symbol dan for...of
Symbol() adalah tipe primitif yang selalu unik, bahkan dengan deskripsi sama. Biasanya dipakai sebagai key properti yang dijamin tidak bentrok.
for...of menelusuri nilai dari iterable apa pun. Bedakan dengan for...in yang menelusuri key objek.
Proxy dan Reflect, sekilas
Proxy membungkus objek untuk menyadap operasi seperti baca dan tulis properti. Reflect menyediakan versi "normal" operasi itu, jadi kamu bisa menambah perilaku lalu meneruskan ke aslinya.
Framework seperti Vue 3 memakai Proxy untuk mendeteksi perubahan data secara otomatis.
Kesalahan umum
- Lupa
returndi dalam.then(). Rantai berikutnya menerimaundefineddan tidak menunggu Promise yang kamu buat. - Tidak menulis
.catch(). Promise yang rejected tanpa penanganan memunculkan peringatan "Unhandled promise rejection". - Memakai
Promise.allsaat sebagian boleh gagal. Satu kegagalan membuang semua hasil; pakaiallSettled. - Memanggil generator lalu mengira hasilnya nilai.
hitungMundur(3)mengembalikan objek generator; ambil nilainya lewatnext(), spread, ataufor...of. - Mengakses Map seperti objek.
nilai["Dini"]bukan cara membaca Map; pakainilai.get("Dini"). - Memakai
for...ofpada objek biasa. Objek{}tidak iterable; pakaiObject.entries(obj)dulu.
Rangkuman
- Asynchronous membuat satu thread tetap responsif; callback bertingkat diganti Promise yang bisa dirangkai dengan
.then,.catch,.finally. allbutuh semua berhasil,allSettledmenunggu semua apa pun hasilnya,raceambil yang tercepat,anyambil yang pertama berhasil.- Generator
function*denganyieldadalah cara ringkas membuat iterator yang lazy. Mapuntuk key-value dengan key bebas,Setuntuk nilai unik,Symboluntuk key unik,for...ofuntuk menelusuri nilai iterable, dan Proxy/Reflect untuk menyadap operasi objek.
Slide 32 sampai 38 dari deck JavaScript. Penjelasan lengkapnya ada di mode Baca.

Selesaikan bab ini
- 1. Baca materi
- 2. Latihan kode (1)
- 3. Kuis (4 soal)
Latihan kode: Hasil aman, rentang, dan hitung kata
Lengkapi tiga fungsi:
async function ambilAman(daftar)menerima array Promise dan mengembalikan array nilainya. Promise yang gagal digantinull. GunakanPromise.allSettled. Contoh: untuk[Promise.resolve(1), Promise.reject(new Error("x"))]hasilnya[1, null].function* rentang(awal, akhir, langkah = 1)adalah generator yang meng-yieldangka dariawalsampaiakhir(termasuk) dengan lompatanlangkah. Contoh:[...rentang(1, 10, 3)]menghasilkan[1, 4, 7, 10].hitungKata(teks)mengembalikanMapberisi kata (dipisah spasi) dan jumlah kemunculannya. Contoh:hitungKata("a b a").get("a")menghasilkan2.
Cek pemahaman
1Urutan apa yang ditampilkan kode berikut?
console.log("A"); setTimeout(() => console.log("B"), 0); Promise.resolve().then(() => console.log("C")); console.log("D");2Kamu memuat tiga widget dan ingin tetap menampilkan yang berhasil walau satu gagal. Method yang paling cocok?
3Apa yang ditampilkan kode berikut?
function* angka() { yield 1; yield 2; yield 2; } const s = new Set(angka()); console.log(s.size);4WeakMap bisa ditelusuri dengan `for...of` dan punya properti `.size`.