Kasir kafe Budi sering menangani beberapa pesanan sekaligus: dua seduhan harus keduanya selesai sebelum nampan diantar, satu layar harus menampilkan laporan lengkap termasuk pesanan yang gagal, dan pemesan berganti meja begitu satu menu tersedia lebih cepat. Satu promise mewakili satu hasil masa depan; menggabungkan beberapa promise menuntut pola yang lebih tinggi.
Untuk kebutuhan itu JavaScript menyediakan empat combinator statis pada Promise — all, allSettled, race, dan any. Semuanya menerima iterable berisi promise dan mengembalikan satu promise baru. Chapter ini membuktikan perilaku keempatnya dengan satu berkas uji, lalu merangkum kapan masing-masing tepat dipakai.
Empat Combinator dalam Satu Berkas #
Berkas berikut memakai satu helper seduh untuk membuat promise pesanan, lalu menjalankan lima percobaan: all sukses, all gagal, allSettled, race, dan any:
// kombinator-promise.js
// Empat combinator: all, allSettled, race, any.
function seduh(nama, ms, gagal = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (gagal) {
reject(new Error(`${nama} gagal`));
} else {
resolve(`${nama} selesai`);
}
}, ms);
});
}
// --- 1. all: berhasil bila SEMUA berhasil ---
Promise.all([seduh("espresso", 30), seduh("latte", 10)])
.then((hasil) => console.log("1. all:", hasil));
// --- 2. all: gagal cepat bila satu saja gagal ---
Promise.all([seduh("espresso", 30), seduh("latte", 10, true)])
.catch((error) => console.log("2. all gagal cepat:", error.message));
// --- 3. allSettled: laporan lengkap, sukses maupun gagal ---
Promise.allSettled([seduh("matcha", 20), seduh("cokelat", 10, true)])
.then((hasil) => {
for (const entri of hasil) {
console.log("3. allSettled:", entri.status, entri.value ?? entri.reason.message);
}
});
// --- 4. race: hasil pertama yang settle, sukses maupun gagal ---
Promise.race([seduh("teh", 10), seduh("kopi", 30)])
.then((hasil) => console.log("4. race:", hasil));
// --- 5. any: hasil pertama yang BERHASIL ---
Promise.any([seduh("susu kedelai", 30), seduh("susu oat", 10, true)])
.then((hasil) => console.log("5. any:", hasil));Output yang Dihasilkan:
2. all gagal cepat: latte gagal
4. race: teh selesai
3. allSettled: fulfilled matcha selesai
3. allSettled: rejected cokelat gagal
1. all: [ 'espresso selesai', 'latte selesai' ]
5. any: susu kedelai selesaiSebelum membedah, perhatikan hal yang mencolok: nomor pada keluaran tidak berurutan. Itu bukan keanehan — keluaran mengikuti waktu selesainya setiap combinator, dan jeda yang kita pasang sengaja dibuat berbeda. Kita bedah satu per satu.
Promise.all: Semua Harus Berhasil #
Promise.all menunggu semua promise fulfilled, lalu fulfilled dengan array berisi seluruh nilai. Urutan nilainya persis urutan input, bukan urutan selesai — baris keluaran 1 menempatkan espresso (30 ms) di posisi pertama meski latte selesai lebih dulu. Bila satu saja rejected, all langsung rejected dengan alasan promise yang gagal lebih dulu. Itulah perilaku fail-fast pada keluaran 2: latte gagal di 10 ms, dan kegagalan itu segera menjadi hasil akhir meski espresso masih menyeduh.
Promise.allSettled: Laporan Lengkap #
Promise.allSettled menunggu semua promise selesai apa pun statusnya, lalu fulfilled dengan array berisi deskripsi tiap hasil: entri fulfilled berbentuk { status, value }, entri rejected berbentuk { status, reason }. Keluaran 3 menampung keduanya berkat ekspresi entri.value ?? entri.reason.message — nilai hasil berada di value, sedangkan pesan kegagalan berada di reason.message. Karena allSettled tidak pernah rejected, penanganannya nyaman: cukup filter berdasarkan status.
Promise.race: Siapa Pun yang Lebih Dulu #
Promise.race mengembalikan hasil promise pertama yang settle — menang atau kalah. Keluaran 4 menunjukkan teh (10 ms) lebih dulu daripada kopi (30 ms). race sama sekali tidak peduli status: bila promise yang lebih dulu settle justru yang gagal, race langsung rejected dengan alasan itu.
Promise.any: Pertama yang Berhasil #
Promise.any menunggu promise pertama yang fulfilled dan mengabaikan rejection selama masih ada yang bisa berhasil. Keluaran 5 memperlihatkannya: susu oat gagal di 10 ms, tetapi any terus menunggu susu kedelai dan fulfilled bersamanya. Bila semua anggota rejected, any rejected dengan AggregateError — berikut bukti nyatanya:
// Semua anggota gagal: any rejected dengan AggregateError.
Promise.any([
Promise.reject(new Error("server 1 gagal")),
Promise.reject(new Error("server 2 gagal")),
]).catch((error) => {
console.log(error.name);
console.log(error.errors.length);
});Output yang Dihasilkan:
AggregateError
2Properti errors milik AggregateError menampung seluruh alasan kegagalan dalam urutan input — berguna saat kita perlu melaporkan mengapa semua sumber gagal.
Memilih Combinator yang Tepat #
| Kombinator | Berhasil bila | Gagal bila | Cocok untuk |
|---|---|---|---|
Promise.all | semua fulfilled | satu saja rejected (fail-fast) | tugas yang semuanya wajib sukses |
Promise.allSettled | selalu fulfilled | tidak pernah | laporan lengkap sukses dan gagal |
Promise.race | promise pertama settle | promise pertama yang settle adalah rejection | timeout, membandingkan kecepatan |
Promise.any | promise pertama yang fulfilled | semua rejected (AggregateError) | fallback ke sumber tercepat yang sukses |
Perilaku fail-fast Promise.all nyaman saat satu kegagalan memang harus membatalkan semuanya — tetapi menyulitkan bila kita tetap ingin memproses yang berhasil. Cara menangani situasi itu dibedah tuntas di chapter Error Handling Asynchronous Code.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
kombinator-promise.js
Unduh berkas// kombinator-promise.js
// Empat combinator: all, allSettled, race, any.
function seduh(nama, ms, gagal = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (gagal) {
reject(new Error(`${nama} gagal`));
} else {
resolve(`${nama} selesai`);
}
}, ms);
});
}
// --- 1. all: berhasil bila SEMUA berhasil ---
Promise.all([seduh("espresso", 30), seduh("latte", 10)])
.then((hasil) => console.log("1. all:", hasil));
// --- 2. all: gagal cepat bila satu saja gagal ---
Promise.all([seduh("espresso", 30), seduh("latte", 10, true)])
.catch((error) => console.log("2. all gagal cepat:", error.message));
// --- 3. allSettled: laporan lengkap, sukses maupun gagal ---
Promise.allSettled([seduh("matcha", 20), seduh("cokelat", 10, true)])
.then((hasil) => {
for (const entri of hasil) {
console.log("3. allSettled:", entri.status, entri.value ?? entri.reason.message);
}
});
// --- 4. race: hasil pertama yang settle, sukses maupun gagal ---
Promise.race([seduh("teh", 10), seduh("kopi", 30)])
.then((hasil) => console.log("4. race:", hasil));
// --- 5. any: hasil pertama yang BERHASIL ---
Promise.any([seduh("susu kedelai", 30), seduh("susu oat", 10, true)])
.then((hasil) => console.log("5. any:", hasil));