mahirasyncpromiseMahir3 mnt baca

Promise.all, allSettled, dan race: Banyak Janji Sekaligus

Operasi independen tidak perlu antre satu per satu. Pelajari cara menjalankan banyak Promise paralel dan memilih strategi yang tepat saat sebagian gagal.

Kenapa Paralel Itu Penting?

Di modul chaining, operasi berjalan berurutan: tahap 2 menunggu tahap 1, benar kalau tahap 2 butuh hasil tahap 1. Tapi kalau operasinya independen, antre itu pemborosan: memuat daftar menu, promo, dan jam buka warung yang tidak saling bergantung satu per satu berarti total waktu = jumlah semua waktu.

Solusinya: jalankan semuanya bersamaan (paralel), lalu tunggu semuanya selesai, dengan tiga alat: Promise.all, Promise.allSettled, dan Promise.race. Memilih yang salah bisa bikin bug halus, jadi pahami bedanya baik-baik.

Promise.all: Semua Harus Sukses

Promise.all menerima array Promise dan mengembalikan satu Promise yang fulfilled dengan array hasil (urut sesuai input) jika semua berhasil; satu saja gagal, keseluruhan langsung rejected (fail-fast):

javascript
function ambilMenu() {
  return new Promise((res) => setTimeout(() => res(["mie", "teh", "kopi"]), 1000));
}
function ambilPromo() {
  return new Promise((res) => setTimeout(() => res("Diskon 10%"), 1500));
}
function ambilJamBuka() {
  return new Promise((res) => setTimeout(() => res("08.00-22.00"), 800));
}

Promise.all([ambilMenu(), ambilPromo(), ambilJamBuka()])
  .then(([menu, promo, jam]) => {
    console.log("Menu:", menu);
    console.log("Promo:", promo);
    console.log("Jam buka:", jam);
  })
  .catch((err) => console.log("Salah satu gagal:", err));

Total waktu tunggu hanya sekitar 1,5 detik (yang paling lambat), bukan 3,3 detik, dan urutan hasil dijamin sama dengan urutan input, bukan urutan selesai.

Gunakan Promise.all ketika semua hasil wajib ada, misalnya profil + keranjang sebelum checkout: satu gagal berarti halaman tidak tampil benar, jadi gagal cepat adalah perilaku yang tepat.

Promise.allSettled: Semua Boleh Dicoba

Kadang kamu butuh semua hasil apa pun yang terjadi, misalnya laporan 5 cabang: cabang 3 offline pun 4 lainnya tetap harus tampil. Di sinilah Promise.allSettled bersinar:

javascript
function ambilCabang(nama, online) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      if (online) res(nama + ": Rp500.000");
      else rej(nama + " offline");
    }, 1000);
  });
}

Promise.allSettled([
  ambilCabang("Cabang A", true),
  ambilCabang("Cabang B", false),
  ambilCabang("Cabang C", true),
]).then((hasil) => {
  hasil.forEach((h) => {
    if (h.status === "fulfilled") console.log("OK:", h.value);
    else console.log("GAGAL:", h.reason);
  });
});

allSettled tidak pernah rejected oleh kegagalan anggotanya; ia selalu fulfilled dengan array object berstatus (status, value/reason).

Catatan teknis: pola ini juga pas untuk "best effort" paralel: kirim notifikasi ke banyak pelanggan dan catat mana yang terkirim, tanpa satu kegagalan menggagalkan semuanya.

Promise.race: Siapa Tercepat Menang

Promise.race selesai mengikuti Promise yang selesai pertama, mengabaikan sisanya. Kegunaan klasiknya adalah timeout: balapan operasi asli melawan timer penolakan:

javascript
function ambilDataLambat() {
  return new Promise((res) => setTimeout(() => res("data"), 5000));
}
function timeout(ms) {
  return new Promise((_, rej) =>
    setTimeout(() => rej("Timeout setelah " + ms + "ms"), ms)
  );
}

Promise.race([ambilDataLambat(), timeout(2000)])
  .then((data) => console.log("Dapat:", data))
  .catch((err) => console.log("Gagal:", err));
// Output: Gagal: Timeout setelah 2000ms

Tanpa race, pengguna menunggu 5 detik tanpa kepastian; dengan race, aplikasi menyerah elegan setelah 2 detik dan menampilkan "coba lagi". Pola timeout ini wajib karena jaringan tidak pernah bisa dipercaya 100%.

Kapan Pakai yang Mana?

  • Promise.all: butuh SEMUA hasil, satu gagal = semuanya gagal. Contoh: data wajib halaman checkout.
  • Promise.allSettled: butuh SEMUA hasil apa pun yang terjadi. Contoh: laporan multi-cabang, pengiriman notifikasi massal.
  • Promise.race: butuh yang tercepat saja. Contoh: timeout, mengambil dari server tercepat di antara beberapa mirror.

Jebakan Umum

  1. Promise.all fail-fast mengejutkan: satu request gagal menghapus seluruh hasil, termasuk yang sudah sukses. Butuh hasil parsial? Pakai allSettled.
  2. race tidak membatalkan yang kalah: Promise yang kalah tetap berjalan di background. Untuk fetch sungguhan, gabungkan dengan AbortController agar request benar-benar dibatalkan.
  3. Mengira hasil all urut menurut waktu selesai: urutan hasil selalu mengikuti urutan array input. Jangan mengandalkan urutan selesai untuk logika.
  4. Promise.all([]) kosong: langsung fulfilled dengan []. Kalau array dibangun dinamis dan ternyata kosong, rantai tetap jalan; pastikan itu memang yang kamu mau.
Live Playground
export default function App(): JSX.Element {
  return <h1>Hello world</h1>
}

Edit kode di kiri, preview kanan ter-update otomatis.

Tantangan

Dashboard Cabang Paralel

Di dunia nyata, dashboard memuat banyak sumber data independen sekaligus; menjalankannya paralel bisa memangkas waktu muat hingga berkali-kali lipat. Memilih all vs allSettled dengan tepat membedakan aplikasi yang rapuh dan yang tangguh. Tugasmu: buat 3 fungsi tiruan ambilOmzet(cabang) yang resolve setelah 1 detik dengan string omzet, tapi cabang "Timur" selalu reject. Gunakan Promise.allSettled untuk mengambil ketiganya, lalu tampilkan ringkasan "X berhasil, Y gagal" 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>