menengahasyncpromiseMenengah3 mnt baca

Promise: Janji yang Ditepati (atau Tidak)

Callback hell butuh obat. Kenalan dengan Promise: object yang mewakili hasil operasi asinkron di masa depan, lengkap dengan tiga statusnya.

Kenapa Promise Lahir?

Di modul callback, kita melihat piramida callback hell: alur asinkron bertahap yang menjorok ke kanan dan sulit dirawat. Promise adalah jawaban bahasa JavaScript atas masalah itu: sebuah object yang mewakili nilai di masa depan. Alih-alih menumpuk callback ke dalam, kamu memegang satu object janji dan merangkai kelanjutannya secara datar dengan .then().

Analoginya sederhana. Janji punya tiga status, persis seperti janji di dunia nyata:

  • pending: janji baru diucapkan, hasilnya belum tahu. ("Aku janji belikan gorengan.")
  • fulfilled: janji ditepati, ada hasilnya. ("Gorengannya datang!")
  • rejected: janji gagal ditepati, ada alasannya. ("Warungnya tutup, maaf.")

Status hanya bisa berubah sekali: dari pending ke fulfilled atau rejected, tidak bisa kembali. Sifat sekali-jalan ini membuat alur program mudah diprediksi.

Membuat Promise: new Promise

Konstruktor Promise menerima satu fungsi (disebut executor) dengan dua parameter: resolve (untuk menepati janji) dan reject (untuk menggagalkannya):

javascript
const janjiGorengan = new Promise((resolve, reject) => {
  const warungBuka = true;

  setTimeout(() => {
    if (warungBuka) {
      resolve("Gorengan hangat 10 biji!");
    } else {
      reject("Warung tutup, janji batal.");
    }
  }, 2000);
});

janjiGorengan
  .then((hasil) => console.log("Janji ditepati: " + hasil))
  .catch((alasan) => console.log("Janji gagal: " + alasan));

.then() menerima callback untuk kasus sukses (fulfilled), sedangkan .catch() menerima callback untuk kasus gagal (rejected). Keduanya mengembalikan Promise baru, fondasi untuk chaining di modul berikutnya.

Contoh Realistis: Cek Stok Barang Warung

Kasir perlu tahu apakah barang tersedia sebelum menjual. Operasi "cek ke gudang" butuh waktu, jadi dibungkus Promise:

javascript
function cekStok(namaBarang) {
  const stokGudang = { "kopi": 12, "teh": 0, "mie": 30 };

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const jumlah = stokGudang[namaBarang];
      if (jumlah === undefined) {
        reject("Barang '" + namaBarang + "' tidak dikenal.");
      } else if (jumlah === 0) {
        reject("Stok '" + namaBarang + "' habis.");
      } else {
        resolve("Stok '" + namaBarang + "': " + jumlah + " pcs.");
      }
    }, 1500);
  });
}

cekStok("teh")
  .then((info) => console.log(info))
  .catch((err) => console.log("Gagal: " + err));
// Output: Gagal: Stok 'teh' habis.

Perhatikan fungsi mengembalikan (return) Promise-nya. Inilah pola standar: fungsi asinkron modern mengembalikan Promise, dan pemanggil merangkai .then()/.catch(). Pemisahan ini rapi: fungsi tidak perlu tahu apa yang terjadi setelahnya.

Catatan teknis: executor Promise berjalan langsung dan sinkron saat new Promise dipanggil. Yang asinkron adalah penyelesaian janjinya (kapan resolve/reject dipanggil), bukan pembuatan objectnya.

Tiga Status dalam Praktik

Memahami status membantu membaca dokumentasi dan error:

javascript
const p = new Promise((resolve) => {
  setTimeout(() => resolve("selesai"), 1000);
});

console.log("langsung:", "pending (masih menunggu)");
p.then((hasil) => console.log("setelah 1 detik:", hasil, "(fulfilled)"));

const gagal = Promise.reject("alasan gagal");
gagal.catch((err) => console.log("status: rejected,", err));

Promise.resolve(nilai) dan Promise.reject(alasan) adalah cara cepat membuat Promise yang langsung fulfilled atau rejected, berguna untuk testing dan nilai default.

Jebakan Umum

  1. Tidak me-return Promise dari fungsi: pemanggil tidak bisa .then() dan rantai putus. Fungsi asinkron harus selalu mengembalikan Promise-nya.
  2. Lupa .catch(): Promise yang rejected tanpa penanganan memunculkan UnhandledPromiseRejection. Selalu tutup rantai dengan .catch().
  3. Mengira Promise bisa dibatalkan: tidak ada tombol cancel bawaan. Setelah dibuat, executor tetap jalan sampai resolve/reject. (Di browser modern ada AbortController untuk fetch, itu mekanisme terpisah.)
  4. Menaruh kode sinkron berat di executor: executor jalan di main thread. Perhitungan berat di sana tetap memblokir halaman; Promise tidak membuatnya otomatis "di thread lain".
Live Playground
export default function App(): JSX.Element {
  return <h1>Hello world</h1>
}

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

Tantangan

Janji Beli Pulsa

Di dunia nyata, Promise adalah fondasi semua operasi asinkron modern: fetch, baca file, query database semuanya mengembalikan Promise. Memahami resolve/reject adalah syarat mutlak sebelum memakai async/await. Tugasmu: buat fungsi beliPulsa(nominal) yang mengembalikan Promise; resolve dengan "Pulsa Rp" + nominal + " berhasil!" jika nominal >= 10000, reject dengan "Nominal terlalu kecil" jika kurang. Uji dengan 25000 (sukses) dan 5000 (gagal), tampilkan hasilnya 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>