Promise: Janji yang Ditepati (atau Tidak)
Callback hell butuh obat. Kenalan dengan Promise: object yang mewakili hasil operasi asinkron di masa depan, lengkap dengan tiga statusnya.
Kenapa Promise Lahir?
Di modul callback, kita melihat piramida callback hell: alur asinkron bertahap yang menjorok ke kanan dan sulit dirawat. Promise adalah jawaban bahasa JavaScript atas masalah itu: sebuah object yang mewakili nilai di masa depan. Alih-alih menumpuk callback ke dalam, kamu memegang satu object janji dan merangkai kelanjutannya secara datar dengan .then().
Analoginya sederhana. Janji punya tiga status, persis seperti janji di dunia nyata:
- pending: janji baru diucapkan, hasilnya belum tahu. ("Aku janji belikan gorengan.")
- fulfilled: janji ditepati, ada hasilnya. ("Gorengannya datang!")
- rejected: janji gagal ditepati, ada alasannya. ("Warungnya tutup, maaf.")
Status hanya bisa berubah sekali: dari pending ke fulfilled atau rejected, tidak bisa kembali. Sifat sekali-jalan ini membuat alur program mudah diprediksi.
Membuat Promise: new Promise
Konstruktor Promise menerima satu fungsi (disebut executor) dengan dua parameter: resolve (untuk menepati janji) dan reject (untuk menggagalkannya):
const janjiGorengan = new Promise((resolve, reject) => {
const warungBuka = true;
setTimeout(() => {
if (warungBuka) {
resolve("Gorengan hangat 10 biji!");
} else {
reject("Warung tutup, janji batal.");
}
}, 2000);
});
janjiGorengan
.then((hasil) => console.log("Janji ditepati: " + hasil))
.catch((alasan) => console.log("Janji gagal: " + alasan));.then() menerima callback untuk kasus sukses (fulfilled), sedangkan .catch() menerima callback untuk kasus gagal (rejected). Keduanya mengembalikan Promise baru, fondasi untuk chaining di modul berikutnya.
Contoh Realistis: Cek Stok Barang Warung
Kasir perlu tahu apakah barang tersedia sebelum menjual. Operasi "cek ke gudang" butuh waktu, jadi dibungkus Promise:
function cekStok(namaBarang) {
const stokGudang = { "kopi": 12, "teh": 0, "mie": 30 };
return new Promise((resolve, reject) => {
setTimeout(() => {
const jumlah = stokGudang[namaBarang];
if (jumlah === undefined) {
reject("Barang '" + namaBarang + "' tidak dikenal.");
} else if (jumlah === 0) {
reject("Stok '" + namaBarang + "' habis.");
} else {
resolve("Stok '" + namaBarang + "': " + jumlah + " pcs.");
}
}, 1500);
});
}
cekStok("teh")
.then((info) => console.log(info))
.catch((err) => console.log("Gagal: " + err));
// Output: Gagal: Stok 'teh' habis.Perhatikan fungsi mengembalikan (return) Promise-nya. Inilah pola standar: fungsi asinkron modern mengembalikan Promise, dan pemanggil merangkai .then()/.catch(). Pemisahan ini rapi: fungsi tidak perlu tahu apa yang terjadi setelahnya.
Catatan teknis: executor Promise berjalan langsung dan sinkron saat
new Promisedipanggil. Yang asinkron adalah penyelesaian janjinya (kapanresolve/rejectdipanggil), bukan pembuatan objectnya.
Tiga Status dalam Praktik
Memahami status membantu membaca dokumentasi dan error:
const p = new Promise((resolve) => {
setTimeout(() => resolve("selesai"), 1000);
});
console.log("langsung:", "pending (masih menunggu)");
p.then((hasil) => console.log("setelah 1 detik:", hasil, "(fulfilled)"));
const gagal = Promise.reject("alasan gagal");
gagal.catch((err) => console.log("status: rejected,", err));Promise.resolve(nilai) dan Promise.reject(alasan) adalah cara cepat membuat Promise yang langsung fulfilled atau rejected, berguna untuk testing dan nilai default.
Jebakan Umum
- Tidak me-return Promise dari fungsi: pemanggil tidak bisa
.then()dan rantai putus. Fungsi asinkron harus selalu mengembalikan Promise-nya. - Lupa
.catch(): Promise yang rejected tanpa penanganan memunculkanUnhandledPromiseRejection. Selalu tutup rantai dengan.catch(). - Mengira Promise bisa dibatalkan: tidak ada tombol cancel bawaan. Setelah dibuat, executor tetap jalan sampai resolve/reject. (Di browser modern ada
AbortControlleruntuk fetch, itu mekanisme terpisah.) - Menaruh kode sinkron berat di executor: executor jalan di main thread. Perhitungan berat di sana tetap memblokir halaman; Promise tidak membuatnya otomatis "di thread lain".
export default function App(): JSX.Element { return <h1>Hello world</h1> }
Tantangan
Janji Beli Pulsa
Di dunia nyata, Promise adalah fondasi semua operasi asinkron modern: fetch, baca file, query database semuanya mengembalikan Promise. Memahami resolve/reject adalah syarat mutlak sebelum memakai async/await. Tugasmu: buat fungsi beliPulsa(nominal) yang mengembalikan Promise; resolve dengan "Pulsa Rp" + nominal + " berhasil!" jika nominal >= 10000, reject dengan "Nominal terlalu kecil" jika kurang. Uji dengan 25000 (sukses) dan 5000 (gagal), tampilkan hasilnya di #app.
<!doctype html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>Halo JS</h1> <p>Buka console preview untuk melihat output.</p> <script src="index.js"></script> </body> </html>