Callback error-first bekerja, tetapi setiap function asinkron menuntut konvensi yang disepakati sendiri-sendiri, dan sarang callback tumbuh begitu langkah bertambah. JavaScript punya jawaban bawaan sejak ES2015: Promise, sebuah objek standar yang mewakili hasil operasi asinkron — lengkap dengan kontrak seragam untuk sukses, gagal, dan pembersihannya.
Promises adalah fondasi seluruh alat modern di section ini: promise chaining, async-await, hingga combinator. Chapter ini membangun pemahamannya dari nol — tiga state yang dimiliki promise, cara membuatnya sendiri dengan new Promise, dan cara menerima hasilnya lewat then, catch, serta finally.
Tiga State pada Satu Promise #
Setiap promise mengikuti mesin status dengan tiga state:
| State | Arti | Dipicu oleh |
|---|---|---|
| pending | pekerjaan masih berjalan, hasil belum ada | kondisi awal setiap promise |
| fulfilled | pekerjaan selesai disertai nilai | pemanggilan resolve(nilai) |
| rejected | pekerjaan gagal disertai alasan | pemanggilan reject(alasan) |
Dua aturan mengikat mesin status ini. Pertama, perpindahan state terjadi sekali saja: dari pending menuju fulfilled atau rejected, tanpa jalan kembali; panggilan resolve maupun reject berikutnya diabaikan. Kedua, state tidak bisa dibaca langsung dari luar — kita tidak “menanyakan” status sebuah promise, melainkan mendaftarkan handler yang runtime panggil begitu state berubah. Tiga handler standarnya: then menerima keberhasilan, catch menangkap kegagalan, dan finally berjalan di kedua jalur.
Membuat Promise dengan new Promise #
Berkas berikut membuat dua promise yang berlawanan: satu menuju fulfilled, satu menuju rejected. Keduanya dibangun dengan pola yang sama, dan keduanya diawasi oleh tiga handler:
// promise-dasar.js
// new Promise: executor, resolve, reject, then, catch, finally.
// --- 1. promise yang akan fulfilled ---
const janjiMinuman = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Kopi susu Budi siap diantar");
}, 20);
});
janjiMinuman
.then((pesan) => console.log("then:", pesan))
.catch((error) => console.log("catch:", error.message))
.finally(() => console.log("finally: status dicatat ke buku kasir"));
// --- 2. promise yang akan rejected ---
const janjiStok = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Stok gula habis"));
}, 20);
});
janjiStok
.then((pesan) => console.log("then:", pesan))
.catch((error) => console.log("catch:", error.message))
.finally(() => console.log("finally: status dicatat ke buku kasir"));
console.log("dua promise dibuat, program tetap berjalan");Output yang Dihasilkan:
dua promise dibuat, program tetap berjalan
then: Kopi susu Budi siap diantar
finally: status dicatat ke buku kasir
catch: Stok gula habis
finally: status dicatat ke buku kasirMari kita telaah baris-baris pentingnya:
- Baris 5–9 (
new Promisepertama):new Promisemenerima executor — function yang runtime panggil seketika dengan dua parameter,resolvedanreject. Executor bertanggung jawab menentukan nasib promise. - Baris 7 (
resolve(...)): memindahkanjanjiMinumanke state fulfilled dengan nilai string. Panggilan ini terjadi di dalam callback timer, 20 milidetik kemudian. - Baris 11–14 (tiga handler pertama):
thenmenerima nilai fulfilled,catchmenampung jalur gagal (tidak terpakai di sini), danfinallymencatat penyelesaian tanpa peduli jalurnya. - Baris 19 (
reject(new Error(...))): memindahkanjanjiStokke state rejected. Alasan berupa objekErroragar jejaknya informatif, bukan sekadar teks. - Baris 23–26 (handler kedua): untuk promise rejected,
thendilewati dancatchyang menerima alasan;finallytetap berjalan — terlihat dari keluaran yang mencetaknya dua kali. - Baris 28 (log sinkron): tercetak lebih dulu daripada semua handler. Handler promise menunggu di antrean microtask, dan antrean itu baru diproses setelah call stack kosong (chapter Event Loop).
Executor Berjalan Seketika #
Satu sifat executor yang sering mengejutkan: ia dieksekusi segera saat new Promise dipanggil, bukan menunggu pekerjaan selesai. Kita buktikan:
// Executor adalah kode sinkron: berjalan sebelum baris berikutnya.
new Promise(() => console.log("executor berjalan seketika"));
console.log("baris sesudahnya");Output yang Dihasilkan:
executor berjalan seketika
baris sesudahnyaLog dari dalam executor mendahului log baris sesudahnya — bukti bahwa executor adalah kode sinkron biasa. Yang asinkron hanyalah kapan resolve atau reject dipanggil, misalnya dari dalam callback timer seperti pada berkas contoh.
Satu Keputusan yang Berlaku #
resolve dan reject boleh sama-sama dipanggil dalam satu executor, tetapi hanya panggilan pertama yang berlaku:
// Panggilan resolve/reject kedua diabaikan tanpa galat.
new Promise((resolve, reject) => {
resolve("diterima lebih dulu");
reject(new Error("terlambat, tidak berlaku"));
})
.then((pesan) => console.log("then:", pesan))
.catch((error) => console.log("catch:", error.message));Output yang Dihasilkan:
then: diterima lebih duluresolve dipanggil lebih dulu, sehingga promise berstatus fulfilled; reject berikutnya diabaikan tanpa memunculkan galat apa pun. Begitu state meninggalkan pending, promise terkunci pada hasilnya — persis aturan mesin status di awal chapter.
Dengan kontrak yang seragam ini, beberapa promise bisa disambung menjadi alur yang rapi tanpa sarang callback. Mekanismenya — perantaraan nilai dan penanganan galat antar mata rantai — kita bedah di chapter Promise Chaining.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
promise-dasar.js
Unduh berkas// promise-dasar.js
// new Promise: executor, resolve, reject, then, catch, finally.
// --- 1. promise yang akan fulfilled ---
const janjiMinuman = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Kopi susu Budi siap diantar");
}, 20);
});
janjiMinuman
.then((pesan) => console.log("then:", pesan))
.catch((error) => console.log("catch:", error.message))
.finally(() => console.log("finally: status dicatat ke buku kasir"));
// --- 2. promise yang akan rejected ---
const janjiStok = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Stok gula habis"));
}, 20);
});
janjiStok
.then((pesan) => console.log("then:", pesan))
.catch((error) => console.log("catch:", error.message))
.finally(() => console.log("finally: status dicatat ke buku kasir"));
console.log("dua promise dibuat, program tetap berjalan");