Promise JavaScript Adalah: Cara Kerja, 3 State, dan Contoh untuk Pemula
Promise JavaScript adalah janji sebuah nilai di masa depan. Pahami 3 state promise, .then() .catch() .finally(), chaining, dan bedanya dengan callback, plus contoh.
Pembuat Hyper Sheets & BelajarWeb.dev
Promise di JavaScript adalah sebuah objek yang mewakili “janji” bahwa suatu nilai akan tersedia di masa depan, hasil dari operasi yang butuh waktu, seperti mengambil data dari internet. Saat kamu memulai operasi async, kamu belum langsung punya hasilnya; yang kamu pegang dulu adalah sebuah Promise, semacam tanda terima yang berkata “nanti hasilnya pasti datang, entah berhasil atau gagal”. Promise lahir di ES2015 untuk menggantikan cara lama yang berantakan (callback bersarang) dan menjadi fondasi di balik async/await yang lebih modern.
Di panduan ini kamu akan memahami Promise benar-benar dari nol: apa itu Promise dan kenapa ia ada, tiga state yang dilaluinya, cara memakai .then(), .catch(), dan .finally(), cara merangkai (chaining) beberapa operasi, sampai perbedaan jelas antara callback, Promise, dan async/await. Semua dengan contoh berbahasa Indonesia yang bisa langsung kamu coba di console browser.
Apa Itu Promise di JavaScript?
Bayangkan kamu memesan makanan lewat aplikasi. Begitu memesan, kamu tidak langsung menerima makanannya, kamu menerima struk pesanan. Struk itu adalah janji: “pesananmu sedang diproses, dan nanti akan berakhir dengan salah satu dari dua kemungkinan: makanan datang (berhasil) atau pesanan dibatalkan (gagal)”. Sambil menunggu, kamu tetap bebas melakukan hal lain.
Itulah persis peran sebuah Promise. Ketika kamu meminta data dari server, JavaScript tidak membekukan halaman untuk menunggu. Ia langsung memberimu sebuah objek Promise, “struk” tadi, lalu melanjutkan pekerjaan lain. Begitu datanya siap (atau gagal), Promise itu “diselesaikan” dan kode yang kamu pasang untuk menanganinya baru dijalankan.
Membuat Promise secara manual terlihat seperti ini:
const janji = new Promise((resolve, reject) => {
// operasi yang butuh waktu...
const berhasil = true
if (berhasil) {
resolve("data berhasil diambil") // tandai sebagai sukses
} else {
reject("gagal mengambil data") // tandai sebagai gagal
}
})
Fungsi di dalam new Promise menerima dua “tuas”: resolve untuk menandai bahwa operasinya berhasil (beserta nilainya), dan reject untuk menandai bahwa operasinya gagal (beserta alasannya). Di praktik sehari-hari kamu jarang menulis new Promise sendiri, fungsi seperti fetch sudah mengembalikan Promise untukmu. Tapi memahami bentuk mentahnya membuat semua konsep berikutnya jauh lebih jelas.
Kenapa Promise Ada? Masalah “Callback Hell”
Sebelum Promise, cara menangani operasi async adalah dengan callback, kamu melempar sebuah fungsi sebagai argumen, dan fungsi itu dipanggil nanti saat hasilnya siap. Untuk satu operasi ini masih oke. Masalah muncul ketika kamu butuh beberapa langkah yang berurutan: ambil user, lalu ambil postingannya, lalu ambil komentarnya. Callback mulai saling bersarang:
ambilUser(function (user) {
ambilPostingan(user.id, function (postingan) {
ambilKomentar(postingan.id, function (komentar) {
console.log(komentar) // kodenya menjorok makin dalam...
})
})
})
Pola ini dijuluki “callback hell” (atau “pyramid of doom”), kode menjorok makin ke kanan, sulit dibaca, dan lebih parah lagi: menangani error di setiap level jadi berantakan. Promise lahir untuk merapikan ini. Dengan Promise, rangkaian yang sama menjadi rantai yang datar dan mudah diikuti:
ambilUser()
.then((user) => ambilPostingan(user.id))
.then((postingan) => ambilKomentar(postingan.id))
.then((komentar) => console.log(komentar))
.catch((error) => console.error(error))
Perhatikan bedanya: alih-alih bersarang ke dalam, langkah-langkahnya berjajar ke bawah, dan satu .catch() di akhir sudah cukup menangani error dari mana pun di rantai itu. Inilah nilai jual utama Promise.
Tiga State Sebuah Promise
Setiap Promise selalu berada di salah satu dari tiga state, dan inilah inti cara kerjanya:
pending, state awal. Operasinya masih berjalan, hasilnya belum ada. Ini seperti struk pesanan yang masih “diproses”.fulfilled, operasi berhasil. Promise sudah punya nilai hasilnya (lewatresolve). Makanan datang.rejected, operasi gagal. Promise punya alasan kegagalannya (lewatreject). Pesanan dibatalkan.
Alurnya selalu satu arah: sebuah Promise mulai dari pending, lalu berpindah sekali ke fulfilled atau rejected. Begitu berpindah, ia dianggap settled (sudah selesai) dan statusnya terkunci, tidak bisa berubah lagi, tidak bisa kembali ke pending.
Konsep tiga state ini paling mudah dipahami kalau kamu bisa memainkannya sendiri. Coba playground di bawah: mulai dari pending, tekan resolve atau reject, dan perhatikan handler mana yang berjalan. Setelah settled, tombolnya terkunci, persis seperti Promise sungguhan.
Coba Sendiri
pendingHandler yang berjalan
.then()Hanya berjalan kalau promise fulfilled..catch()Hanya berjalan kalau promise rejected..finally()Akan berjalan begitu promise settled (fulfilled maupun rejected).const janji = new Promise((resolve, reject) => {
// ...belum dipanggil, masih menunggu
})Setelah bermain dengan playground di atas, kamu bisa mendalami konsep yang sama dengan latihan bertahap lewat pelajaran fetch & promises di Hyper Sheets yang bisa langsung dikerjakan di browser tanpa setup apa pun.
Menangani Hasil: .then(), .catch(), dan .finally()
Sebuah Promise tidak berguna sampai kamu memasang “penangkap” hasilnya. Ada tiga method utama untuk ini, dan masing-masing berjalan pada kondisi yang berbeda.
.then() berjalan ketika Promise fulfilled, dan menerima nilai hasilnya sebagai argumen:
janji.then((nilai) => {
console.log("Berhasil:", nilai)
})
.catch() berjalan ketika Promise rejected, dan menerima alasan errornya:
janji.catch((error) => {
console.error("Gagal:", error)
})
.finally() selalu berjalan setelah Promise settled, mau berhasil maupun gagal. Ia tidak menerima nilai, dan cocok untuk pekerjaan pembersihan (cleanup) yang harus dilakukan apa pun hasilnya:
janji.finally(() => {
console.log("Proses selesai, apa pun hasilnya")
})
Ketiganya biasanya dirangkai sekaligus dalam satu rantai yang rapi:
fetch("https://api.example.com/user")
.then((response) => response.json())
.then((data) => console.log("Data:", data))
.catch((error) => console.error("Ada masalah:", error))
.finally(() => console.log("Selesai memuat"))
Chaining: Merangkai Beberapa Promise
Kekuatan sesungguhnya Promise muncul saat kamu merangkainya (chaining). Kuncinya: setiap .then() mengembalikan Promise baru, sehingga kamu bisa menyambung .then() berikutnya. Dan kalau di dalam sebuah .then() kamu mengembalikan sebuah nilai, nilai itu diteruskan ke .then() selanjutnya:
ambilAngka() // Promise yang resolve ke 5
.then((angka) => angka * 2) // teruskan 10
.then((angka) => angka + 1) // teruskan 11
.then((hasil) => console.log(hasil)) // 11
Yang lebih berguna: kalau sebuah .then() mengembalikan Promise lain, rantai akan menunggu Promise itu selesai dulu sebelum lanjut. Inilah yang membuat operasi berurutan (seperti ambil user → ambil postingan) jadi rapi tanpa bersarang:
ambilUser()
.then((user) => ambilPostingan(user.id)) // kembalikan Promise
.then((postingan) => console.log(postingan)) // menunggu yang di atas selesai
.catch((error) => console.error(error)) // menangkap error dari langkah mana pun
Satu .catch() di akhir menangkap error yang terjadi di langkah mana pun dalam rantai, jauh lebih bersih daripada mengecek error di setiap level seperti pada callback.
Callback vs Promise vs Async/Await
Ini bagian yang paling sering dicari: apa sebenarnya bedanya ketiga pendekatan ini? Semuanya menyelesaikan masalah yang sama, menangani operasi async, tapi lahir di generasi berbeda dan makin lama makin nyaman dipakai.
| Aspek | Callback | Promise | Async/Await |
|---|---|---|---|
| Muncul sejak | Awal JavaScript | ES2015 | ES2017 |
| Keterbacaan | Buruk (menjorok ke kanan) | Sedang (rantai .then()) |
Terbaik (seperti kode biasa) |
| Chaining / nesting | Bersarang makin dalam | Rantai .then() yang datar |
Baris demi baris, tanpa rantai |
| Error handling | if (err) manual di tiap callback |
Satu .catch() di ujung |
try/catch biasa |
| Masalah utama | “Callback hell” | Rantai bisa panjang | Hampir tidak ada |
Perhatikan pergerakannya: dari callback yang bersarang ke kanan, ke Promise yang merapikannya jadi rantai datar, lalu ke async/await yang membuat kode async terbaca seperti kode sinkron biasa. Penting dipahami bahwa ketiganya bukan teknologi yang benar-benar berbeda, async/await sebenarnya dibangun di atas Promise. Ia hanya cara penulisan yang lebih manusiawi untuk Promise yang sama:
// Gaya Promise (.then)
fetch("https://api.example.com/user")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error))
// Gaya async/await, hasil identik, terbaca dari atas ke bawah
async function ambilUser() {
try {
const response = await fetch("https://api.example.com/user")
const data = await response.json()
console.log(data)
} catch (error) {
console.error(error)
}
}
Karena async/await dibangun di atas Promise, memahami Promise dulu membuat async/await terasa jauh lebih masuk akal. Kalau kamu sudah nyaman dengan tiga state dan .then()/.catch() di atas, langkah berikutnya yang paling logis adalah mempelajari async/await sebagai cara menulis Promise yang lebih rapi, di sana kamu akan lihat bagaimana await sebenarnya “membuka” nilai dari dalam Promise.
Kesalahan Umum Pemula soal Promise
Beberapa jebakan yang hampir selalu dialami saat pertama kali belajar Promise:
1. Lupa memasang .catch(). Seperti dibahas di atas, Promise yang gagal tanpa .catch() menghasilkan error yang tertelan diam-diam. Selalu tutup rantaimu dengan .catch().
2. Lupa return di dalam .then(). Kalau kamu mau meneruskan hasil ke .then() berikutnya, kamu harus return nilainya. Tanpa return, .then() selanjutnya menerima undefined.
ambilUser()
.then((user) => { ambilPostingan(user.id) }) // ❌ lupa return
.then((postingan) => console.log(postingan)) // undefined!
3. Mengira Promise “menunggu” sebelum dijalankan. Promise bersifat eager, operasinya langsung jalan saat dibuat, bukan saat .then() dipasang.
4. Menyarangkan .then() seperti callback. Sebagian pemula menaruh .then() di dalam .then(), menghidupkan kembali callback hell. Manfaatkan chaining datar: kembalikan Promise dari dalam .then() lalu sambung .then() berikutnya di level yang sama.
5. Mengira .then() dan .catch() berjalan langsung (sinkron). Handler Promise selalu dijalankan secara async, walau Promise-nya sudah settled, .then()-nya baru jalan setelah kode sinkron di sekitarnya selesai.
Handler mana yang berjalan ketika sebuah Promise berpindah ke state rejected?
FAQ Seputar Promise JavaScript
Apa itu Promise dengan bahasa sederhana?
Promise adalah “janji” di JavaScript bahwa sebuah nilai akan tersedia nanti, hasil dari sesuatu yang butuh waktu, seperti mengambil data dari internet. Ibaratnya struk pesanan makanan: kamu belum memegang makanannya, tapi kamu punya tanda bahwa pesananmu sedang diproses dan nanti akan berakhir berhasil (makanan datang) atau gagal (pesanan dibatalkan). Sambil menunggu, program tetap bisa mengerjakan hal lain tanpa membeku.
Apa itu 3 state dalam Promise?
Sebuah Promise selalu berada di salah satu dari tiga state: pending (masih menunggu, hasil belum ada), fulfilled (berhasil, punya nilai hasilnya), dan rejected (gagal, punya alasan kegagalannya). Promise selalu mulai dari pending lalu berpindah sekali saja ke fulfilled atau rejected. Setelah berpindah, ia dianggap settled dan statusnya terkunci, tidak bisa berubah lagi.
Apa beda .then(), .catch(), dan .finally()?
.then() berjalan saat Promise berhasil (fulfilled) dan menerima nilai hasilnya. .catch() berjalan saat Promise gagal (rejected) dan menerima errornya. .finally() selalu berjalan setelah Promise settled, mau berhasil maupun gagal, dan cocok untuk pekerjaan pembersihan seperti menyembunyikan indikator loading. Ketiganya biasa dirangkai dalam satu rantai.
Apa bedanya Promise dan async/await?
Keduanya bukan saingan, async/await sebenarnya dibangun di atas Promise. Promise ditangani dengan rantai .then()/.catch(), sedangkan async/await adalah cara penulisan yang lebih rapi untuk Promise yang sama, sehingga kodenya terbaca dari atas ke bawah seperti kode biasa. Setiap fungsi async sebenarnya mengembalikan sebuah Promise. Pelajari lebih lanjut di panduan async/await.
Berapa kali sebuah Promise bisa berubah status?
Hanya sekali. Sebuah Promise mulai dari pending dan berpindah tepat satu kali ke fulfilled atau rejected, lalu terkunci selamanya. Memanggil resolve() atau reject() lagi setelah itu tidak akan berpengaruh apa pun. Sifat “settles sekali dan immutable” inilah yang membuat Promise bisa diandalkan.
Setelah Promise, Belajar Apa?
Setelah nyaman dengan Promise dan tiga state-nya, berikut jalur lanjutan yang masuk akal:
async/await, cara modern menulis Promise agar terbaca seperti kode biasa. Ini langkah wajib berikutnya; mulai dari panduan async/await.fetchdan REST API, hampir semua penggunaan Promise di dunia nyata melibatkan pengambilan data dari API. Latih pola.then()+.catch()sampai jadi refleks lewat pelajaran fetch & promises.Promise.all,Promise.race,Promise.allSettled, untuk mengatur banyak Promise sekaligus dengan strategi berbeda (menunggu semua, mengambil yang tercepat, dan lain-lain).- Error handling lanjutan, membuat pesan error yang ramah pengguna dan mekanisme retry otomatis.
Promise adalah salah satu konsep yang membuka pintu ke seluruh JavaScript modern, dari async/await sampai data fetching di React. Kalau fondasi bahasanya masih goyah, ada baiknya mengulang dulu panduan JavaScript dari nol sebelum menyelam lebih dalam ke async. Kuasai konsep tiga state dan chaining pelan-pelan, banyak praktik langsung di console browser, dan jangan takut membaca pesan error, di situlah proses belajar yang sesungguhnya terjadi.
Siap praktik langsung? Kerjakan pelajaran fetch & promises interaktif di Hyper Sheets yang membimbingmu menangani Promise sungguhan langkah demi langkah, langsung di browser tanpa perlu setup apa pun.