Soal 1Kalau delay(100, "A").then((v) => console.log(v)); diikuti console.log("B");, bagaimana urutan output-nya?
Promise — Menerima Hasil Async dengan then dan catch
Terima hasil proses yang memakan waktu lewat Promise: 3 state-nya, then / catch / finally, chaining, dan Promise.all.
Ada proses yang butuh waktu sampai hasilnya datang, seperti mengirim order atau mengecek stok. Supaya halaman tidak macet selama menunggu, kode untuk proses seperti ini langsung lanjut ke baris berikutnya, lalu hasilnya diambil nanti. Cara menulis seperti ini disebut pemrosesan asynchronous.
Artikel ini membahas Promise, object yang mewakili hasil yang baru tersedia nanti, dan Promise.all, yang menunggu beberapa Promise sekaligus.
Menerima Hasilnya Nanti — Promise dan then
Misalnya jawaban soal ketersediaan kursi baru datang 100ms setelah kamu bertanya. setTimeout (function yang memanggil sebuah function satu kali setelah jumlah milidetik yang ditentukan) langsung selesai begitu dipanggil, dan eksekusi lanjut ke baris berikutnya, jadi jawaban yang baru datang 100ms kemudian tidak bisa kamu return dari function-mu.
Sebagai gantinya, kamu mengembalikan Promise (object yang mewakili hasil yang baru tersedia nanti), lalu jawabannya diantarkan lewat Promise itu. Function yang kamu berikan ke new Promise((resolve) => { ... }) langsung dipanggil saat itu juga, dan memanggil resolve(value) di dalamnya mengubah Promise dari pending (menunggu hasil) menjadi fulfilled (berhasil). Function yang kamu berikan ke method then dipanggil dengan value itu begitu Promise-nya fulfilled.
// Mengembalikan Promise yang fulfilled dengan value setelah ms milidetik
function delay(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
// Mulai mengecek (di titik ini masih pending)
const seats = delay(100, "Sisa 12 kursi");
// Berikan function yang dipanggil begitu fulfilled
seats.then((message) => {
console.log(message); // Sisa 12 kursi (tampil ke-2)
});
console.log("Mengecek ketersediaan kursi"); // Mengecek ketersediaan kursi (tampil ke-1)
then hanya menyimpan function-nya; function itu tidak dipanggil saat itu juga. Function yang kamu berikan baru jalan setelah baris-baris di bawahnya selesai dan Promise-nya fulfilled.Kalau kode yang memakai hasilnya kamu tulis di luar then, kode itu jalan sebelum hasilnya keluar, jadi value-nya belum bisa dipakai. Kumpulkan baris yang memakai hasilnya di dalam function yang kamu berikan ke then, dan taruh hal yang mau kamu kerjakan selama menunggu di baris setelahnya, di luar then.
Tambahkan Baris Penunggu di Akhir agar Output then Muncul
Code runner di halaman ini hanya mengambil output yang tampil sebelum baris terakhir selesai. Di browser atau Node.js, function then tetap jalan walaupun tidak ada yang menunggunya, tapi di halaman ini, tambahkan await delay(1000); di akhir untuk menunggu 1 detik. await (keyword yang menahan eksekusi di baris itu sampai hasil Promise keluar) dibahas di artikel berikutnya.
Menangani Kegagalan — reject / catch / finally
Verifikasi PIN kartu bisa gagal kalau nomornya salah. Kalau yang kamu punya hanya resolve, kegagalan pun datang sebagai value biasa, sama seperti keberhasilan, jadi function penerimanya harus selalu memakai if untuk membedakan keduanya.
Function yang diberikan ke new Promise juga bisa menerima reject sebagai parameter ke-2. Kalau kamu memanggilnya, seperti reject(new Error("pesan")), Promise-nya menjadi rejected (gagal). Function yang diberikan ke catch menerima Error itu saat Promise-nya rejected, dan function yang diberikan ke finally dipanggil paling akhir, baik berhasil maupun gagal.
function verifyPin(pin) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (pin === "4821") {
resolve("PIN cocok");
} else {
// Beri tahu kegagalan dengan memberikan Error ke reject
reject(new Error("PIN salah"));
}
}, 100);
});
}
// then, catch, dan finally bisa disambung berurutan (cara kerjanya dibahas di bagian berikutnya)
verifyPin("0000")
.then((message) => console.log(message)) // tidak dipanggil, karena rejected
.catch((error) => console.log(error.message)) // PIN salah
.finally(() => console.log("Kolom input dikosongkan"));
// Kolom input dikosongkan (tampil paling akhir, baik berhasil maupun gagal)
Saat Promise rejected, function then yang disambung sebelum catch dilewati, dan function catch setelahnya yang dipanggil. catch ini adalah method Promise, berbeda dari statement try / catch. Kalau kamu lupa memanggil resolve maupun reject, kodenya akan terlihat macet tanpa output dan tanpa error.
Menjalankan Langkah Secara Berurutan — Chaining then dan return
Misalnya setelah order dibuat, kamu mau mengirim email konfirmasi memakai nomor order yang dikembalikan. Kalau request ke-2 kamu tulis di dalam then yang pertama, lalu request ke-3 di dalamnya lagi, indentasinya makin dalam di setiap langkah dan kodenya jadi susah dibaca.
then mengembalikan Promise baru, jadi kamu bisa menyambungnya seperti .then(...).then(...). Ini disebut chain (menyambungkan then berikutnya ke Promise yang dikembalikan then sebelumnya). Value yang kamu return dari function then diterima oleh function then berikutnya.
// delay sama dengan kode di bagian pertama
const placeOrder = () => delay(100, "A-2046");
const sendMail = (orderId) => delay(100, `Email konfirmasi untuk ${orderId} sudah dikirim`);
placeOrder()
.then((orderId) => {
console.log(`Nomor order: ${orderId}`); // Nomor order: A-2046
return sendMail(orderId); // chain menunggu sampai email terkirim
})
.then((result) => {
console.log(result); // Email konfirmasi untuk A-2046 sudah dikirim
return "Semua langkah selesai"; // value-nya langsung diteruskan ke function berikutnya
})
.then((status) => console.log(status)); // Semua langkah selesai
Tanpa return, function mengembalikan undefined, jadi then berikutnya jalan tanpa menunggu email terkirim, dan result bernilai undefined. Tidak ada error yang muncul; hasil pengirimannya hilang begitu saja, sehingga kesalahan ini mudah terlewat.
Kalau Pakai Kurung Kurawal, return Tidak Boleh Dihilangkan
Arrow function tanpa kurung kurawal, seperti .then((id) => sendMail(id)), mengembalikan Promise dari sendMail apa adanya, jadi then berikutnya menunggu. Kalau kamu membungkus isinya dengan kurung kurawal untuk menambahkan baris log di tengah, tambahkan lagi return, seperti return sendMail(id);.
Menunggu Beberapa Hasil Sekaligus — Promise.all
Misalnya saat dashboard admin dibuka, kamu meminta data penjualan hari ini, jumlah order, dan jumlah pertanyaan secara terpisah, lalu menampilkan panel ringkasan setelah ketiganya datang. Kalau kamu memasang then terpisah di masing-masing dari ketiga Promise itu, mana yang datang paling akhir bisa berbeda setiap kali, jadi tidak ada satu tempat pun untuk mengecek apakah semuanya sudah datang.
Kalau ketiga function itu kamu panggil lalu Promise hasilnya kamu berikan ke Promise.all (method yang menerima array Promise dan menunggu sampai semua hasilnya datang), ketiga hasil itu diterima sekaligus dalam bentuk array. Kalau ada satu saja yang rejected, alurnya masuk ke catch, bukan ke then.
// delay sama dengan kode di bagian pertama
const loadSales = () => delay(100, 184000);
const loadOrders = () => delay(50, 42);
const loadInquiries = () => delay(80, 3);
// Ketiganya mulai begitu function-nya dipanggil. Promise.all menunggu sampai semuanya datang
Promise.all([loadSales(), loadOrders(), loadInquiries()])
// Terima array hasilnya dengan destructuring berdasarkan posisi
.then(([sales, orders, inquiries]) => {
// Diterima sesuai urutan di array, bukan urutan selesainya
console.log(`Penjualan: ${sales} yen`); // Penjualan: 184000 yen
console.log(`Order: ${orders}`); // Order: 42
console.log(`Pertanyaan: ${inquiries}`); // Pertanyaan: 3
})
.catch((error) => console.log(error.message));
Promise.all sendiri tidak memulai request apa pun; method ini hanya menunggu sampai semuanya datang. Tabel di bawah menunjukkan state Promise yang dikembalikan Promise.all dan function yang dipanggil, sesuai state Promise yang diberikan.
| State Promise yang diberikan | Promise yang dikembalikan Promise.all | Yang dipanggil dan value-nya |
|---|---|---|
| Semua fulfilled | Fulfilled saat yang terakhir fulfilled | then menerima array sesuai urutan |
| Satu rejected | Rejected saat yang satu itu rejected | catch menerima Error tersebut |
| 2 atau lebih rejected | Rejected saat yang pertama rejected | catch hanya menerima Error yang pertama |
| Lainnya fulfilled, satu pending | Tetap pending | then maupun catch tidak dipanggil |
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Kalau kamu menyambung .then(A).catch(B).finally(C) ke Promise yang akhirnya rejected, mana yang dipanggil?
Soal 3Array apa yang diterima then dari Promise.all([delay(100, "A"), delay(50, "B")])?