menengahasyncpromiseMenengah3 mnt baca

Promise Chaining: Merangkai .then() Berantai

Kekuatan Promise ada di rantainya: setiap .then() mengembalikan Promise baru. Pelajari cara mengalirkan dan mentransformasi data tahap demi tahap.

Kenapa Chaining Penting?

Di modul sebelumnya, satu Promise menangani satu operasi. Dunia nyata jarang sesederhana itu: data diambil dari server, lalu difilter, lalu dihitung totalnya, lalu ditampilkan. Tanpa chaining, kamu kembali menumpuk .then() ke dalam seperti callback hell versi baru.

Kuncinya ada di satu fakta: .then() selalu mengembalikan Promise baru. Promise baru itu "menunggu" apa pun yang kamu kembalikan dari callback: nilai biasa langsung menjadi hasil fulfilled, sedangkan Promise lain membuat rantai menunggu sampai Promise itu selesai. Mekanisme inilah yang membuat rantai datar dan terbaca dari atas ke bawah.

Rantai Paling Sederhana

javascript
Promise.resolve(5)
  .then((angka) => {
    console.log("tahap 1:", angka);
    return angka * 2; // nilai biasa -> Promise baru fulfilled dengan 10
  })
  .then((angka) => {
    console.log("tahap 2:", angka);
    return angka + 10; // -> 20
  })
  .then((angka) => {
    console.log("tahap 3:", angka); // 20
  });

Setiap tahap menerima hasil tahap sebelumnya sebagai parameter. Tidak ada nesting, tidak ada indentasi menjorok: alurnya mengalir vertikal seperti membaca resep.

Mengembalikan Promise di Tengah Rantai

Ketika callback mengembalikan Promise lain, rantai menunggu Promise itu selesai dulu. Inilah cara merangkai operasi asinkron berurutan tanpa piramida:

javascript
function ambilDataPengguna(id) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id: id, nama: "Budi" }), 1000);
  });
}

function ambilPesanan(nama) {
  return new Promise((resolve) => {
    setTimeout(() => resolve([nama + ": mie goreng", nama + ": es teh"]), 1000);
  });
}

ambilDataPengguna(7)
  .then((user) => {
    console.log("Pengguna:", user.nama);
    return ambilPesanan(user.nama); // rantai menunggu Promise ini
  })
  .then((pesanan) => {
    console.log("Pesanan:", pesanan);
  })
  .catch((err) => console.log("Ada yang gagal:", err));

Satu .catch() di ujung menangkap error dari tahap mana pun di atasnya. Inilah keunggulan besar dibanding callback: penanganan error terpusat, tidak perlu ditulis di setiap tahap.

Contoh Realistis: Alur Kasir Warung Bertahap

Transformasi data bertahap adalah kasus paling umum. Setiap .then() mengubah bentuk data sedikit demi sedikit sampai siap ditampilkan:

javascript
function ambilKeranjang() {
  return Promise.resolve([
    { nama: "mie goreng", harga: 12000 },
    { nama: "es teh", harga: 5000 },
    { nama: "gorengan", harga: 2000 },
  ]);
}

ambilKeranjang()
  .then((items) => {
    const total = items.reduce((sum, i) => sum + i.harga, 0);
    return { items, total }; // teruskan object ke tahap berikut
  })
  .then((data) => {
    const diskon = data.total > 15000 ? data.total * 0.1 : 0;
    return { ...data, diskon, bayar: data.total - diskon };
  })
  .then((struk) => {
    console.log("Total: Rp" + struk.total);
    console.log("Diskon: Rp" + struk.diskon);
    console.log("Bayar: Rp" + struk.bayar);
  })
  .catch((err) => console.log("Kasir error:", err));

Perhatikan pola return { ...data, ... }: setiap tahap meneruskan data lama plus hasil barunya. Data mengalir seperti di ban berjalan pabrik, setiap stasiun menambah sesuatu.

Catatan teknis: .then() tanpa return membuat tahap berikutnya menerima undefined. Ini bukan error, tapi 90% bug chaining berasal dari return yang terlupa. Kalau tahap berikutnya dapat undefined misterius, cek return tahap sebelumnya dulu.

Jebakan Umum

  1. Lupa return: tahap berikutnya menerima undefined. Selalu return nilai atau Promise dari setiap callback .then().
  2. Nesting .then() di dalam .then(): menggagalkan tujuan chaining. Kembalikan Promise-nya dan rangkai datar di luar.
  3. .then() dengan dua callback vs .catch(): .then(sukses, gagal) hanya menangkap error dari Promise sebelumnya, bukan dari callback suksesnya sendiri. .catch() di ujung rantai lebih aman.
  4. Mengubah data asli antar tahap: mutasi object yang diteruskan membuat tahap sulit dilacak. Lebih aman membuat object baru (spread) seperti contoh kasir di atas.
Live Playground
export default function App(): JSX.Element {
  return <h1>Hello world</h1>
}

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

Tantangan

Rantai Nilai Ujian

Di dunia nyata, chaining dipakai untuk pipeline data: ambil data mentah, bersihkan, hitung, tampilkan. Pola transformasi bertahap ini muncul di dashboard, laporan, dan kasir. Tugasmu: mulai dari Promise.resolve(70), buat rantai 3 tahap: tambah 10 (nilai tugas), kali 1.1 lalu bulatkan dengan Math.round (bonus 10%), lalu tampilkan "Nilai akhir: X" di #app. Akhiri dengan .catch.

<!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>