Rantai then sudah menyembunyikan sarang callback, tetapi masih menuntut kita berpikir dalam bentuk handler: setiap langkah ditulis sebagai function yang menerima hasil langkah sebelumnya. async dan await — tersedia sejak ES2017 — menyediakan sintaks yang tetap dibangun di atas promise, namun terbaca dari atas ke bawah seperti kode sinkron. Bentuk inilah yang paling sering kita pakai sehari-hari, termasuk saat memanggil fetch() untuk mengambil data jaringan di section Network.

Kabar baiknya, tidak ada mesin baru yang perlu dipelajari. async-await adalah gula sintaksis di atas promise yang sudah kita kuasai — state, then, dan catch tetap bekerja di balik layar.

async Mengubah Function Menjadi Promise #

Menambahkan kata async di depan function mengubah kontrak kembaliannya:

  • Function async selalu mengembalikan promise, apa pun isi return-nya.
  • Nilai yang di-return dibungkus menjadi nilai fulfilled.
  • Error yang dilempar di dalamnya menjadi alasan rejected.

Ketiga poin ini kita buktikan langsung di bagian 1 berkas contoh. Perhatikan bahwa pemanggil ambilMenu() tidak menerima string, melainkan instance Promise — untuk membaca isinya, pemanggil tetap memakai then, atau await yang kita bahas sesudah ini.

await: Jeda di Dalam Function #

await hanya sah di dalam function async. Ketika runtime menemui await diikuti promise, eksekusi function itu dijeda sampai promise selesai, dan nilainya diteruskan ke variabel. Yang dijeda hanya function pemanggilnya — thread dan kode lain tetap berjalan, sesuai aturan event loop.

Berkas contoh berikut merangkum tiga perilaku: kembalian async function, dua await berurutan, dan try-catch untuk kegagalan:

// async-await-dasar.js
// async function, await, dan try-catch.

function tunggu(ms, nilai) {
  return new Promise((resolve) => setTimeout(() => resolve(nilai), ms));
}

// --- 1. async function selalu mengembalikan Promise ---
async function ambilMenu() {
  return "Kopi susu, teh tarik, roti bakar";
}

const hasil = ambilMenu();
console.log("kembalian ambilMenu:", hasil instanceof Promise);

hasil.then((menu) => console.log("isi promise:", menu));

// --- 2. await: menunggu promise di dalam async function ---
async function layaniBudi() {
  const minuman = await tunggu(20, "kopi susu");
  const makanan = await tunggu(20, "roti bakar");
  console.log("Budi menerima:", minuman, "+", makanan);
}

layaniBudi();

// --- 3. try-catch menangkap reject dari await ---
async function layaniSiti() {
  const pesanan = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("Stok teh tarik habis")), 10);
  });
  try {
    const minuman = await pesanan;
    console.log("Siti menerima:", minuman);
  } catch (error) {
    console.log("Siti:", error.message);
  }
}

layaniSiti();

Output yang Dihasilkan:

text
kembalian ambilMenu: true
isi promise: Kopi susu, teh tarik, roti bakar
Siti: Stok teh tarik habis
Budi menerima: kopi susu + roti bakar

Mari kita telaah baris-baris pentingnya:

  1. Baris 9–11 (ambilMenu): function biasa yang ditandai async; isi return-nya hanya string biasa.
  2. Baris 13–14 (pemanggilan dan uji): instanceof Promise menghasilkan true — runtime membungkus kembalian function async menjadi promise secara otomatis.
  3. Baris 16 (membaca isi): pemanggil membaca nilai lewat then, bukti bahwa kembalian ambilMenu memang promise biasa yang bisa dirantai.
  4. Baris 20–21 (layaniBudi): dua await berurutan. Baris kedua baru dieksekusi setelah baris pertama selesai, sehingga total waktunya adalah jumlah kedua jeda.
  5. Baris 29–31 (pesanan Siti): promise yang sengaja dibuat gagal lewat reject dari callback timer.
  6. Baris 32–37 (try-catch): await yang menemui promise rejected berperilaku seperti throw — blok catch menangkapnya. Dengan begitu, try-catch dari section Error Handling kembali bekerja untuk kode asinkron.
  7. Baris 40 (pemanggilan tanpa await): layaniBudi() dan layaniSiti() dipanggil tanpa await — sah, karena function async berjalan sendiri; di sini kita sengaja tidak menunggu hasilnya.

await pada Nilai yang Bukan Promise #

await tidak menuntut promise. Bila nilai yang mengikutinya bukan promise, nilai itu diteruskan begitu saja tanpa jeda:

js
async function cobaAwaitNilaiBiasa() {
  const angka = await 42; // bukan promise: langsung diteruskan
  console.log("await pada nilai biasa:", angka);
}

cobaAwaitNilaiBiasa();

Output yang Dihasilkan:

text
await pada nilai biasa: 42

Sifat ini membuat await aman dipakai pada function yang kembaliannya kadang promise, kadang nilai biasa — tidak perlu memeriksa tipe secara manual terlebih dahulu.

Berurutan vs Paralel #

Dua await yang ditulis berurutan berjalan satu demi satu. Bila kedua pekerjaan tidak saling bergantung, urutan seperti ini membuang waktu: pekerjaan kedua sebenarnya bisa dimulai bersamaan. Cara paralelnya sederhana — mulai kedua promise lebih dulu, lalu await keduanya sekaligus dengan Promise.all:

js
function tunggu(ms, nilai) {
  return new Promise((resolve) => setTimeout(() => resolve(nilai), ms));
}

async function berurutan() {
  const awal = Date.now();
  const a = await tunggu(20, "A");
  const b = await tunggu(20, "B");
  return { hasil: a + b, durasi: Date.now() - awal };
}

async function paralel() {
  const awal = Date.now();
  const [a, b] = await Promise.all([tunggu(20, "A"), tunggu(20, "B")]);
  return { hasil: a + b, durasi: Date.now() - awal };
}

async function main() {
  const hasilBerurutan = await berurutan();
  const hasilParalel = await paralel();

  console.log("berurutan:", hasilBerurutan.hasil);
  console.log("paralel  :", hasilParalel.hasil);
  console.log(
    "durasi berurutan lebih lama dari paralel:",
    hasilBerurutan.durasi > hasilParalel.durasi
  );
}

main();

Output yang Dihasilkan:

text
berurutan: AB
paralel  : AB
durasi berurutan lebih lama dari paralel: true

Hasil kedua gaya sama, tetapi waktu berurutan sekitar dua kali lipat waktu paralel. Durasi dalam milidetik memang bervariasi antar mesin, itulah alasan keluaran membandingkan keduanya lewat boolean, bukan angka. Aturan praktisnya: await berurutan untuk langkah yang saling bergantung, Promise.all untuk pekerjaan mandiri. Kombinator lainnya dibedah khusus di chapter Promise Combinators.

Aturan await yang Perlu Diingat #

Empat poin berikut menutup celah kebingungan yang paling sering muncul:

  1. await hanya sah di dalam function async.
  2. await pada nilai bukan promise tetap menghasilkan nilai itu.
  3. Error di dalam function async — baik lewat throw maupun await yang gagal — menjadi promise rejected yang harus ditangkap pemanggilnya dengan catch atau try-catch.
  4. Lupa menangkap rejection berarti membiarkan kegagalan tanpa penanganan; akibatnya dibedah tuntas di chapter Error Handling Asynchronous Code.

Dengan async-await, menulis alur asinkron terasa seperti menulis kode sinkron. Tinggal satu kelompok alat yang belum kita sentuh: menggabungkan banyak promise sekaligus — all, allSettled, race, dan any.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

async-await-dasar.js

Unduh berkas
javascript
// async-await-dasar.js
// async function, await, dan try-catch.

function tunggu(ms, nilai) {
  return new Promise((resolve) => setTimeout(() => resolve(nilai), ms));
}

// --- 1. async function selalu mengembalikan Promise ---
async function ambilMenu() {
  return "Kopi susu, teh tarik, roti bakar";
}

const hasil = ambilMenu();
console.log("kembalian ambilMenu:", hasil instanceof Promise);

hasil.then((menu) => console.log("isi promise:", menu));

// --- 2. await: menunggu promise di dalam async function ---
async function layaniBudi() {
  const minuman = await tunggu(20, "kopi susu");
  const makanan = await tunggu(20, "roti bakar");
  console.log("Budi menerima:", minuman, "+", makanan);
}

layaniBudi();

// --- 3. try-catch menangkap reject dari await ---
async function layaniSiti() {
  const pesanan = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("Stok teh tarik habis")), 10);
  });
  try {
    const minuman = await pesanan;
    console.log("Siti menerima:", minuman);
  } catch (error) {
    console.log("Siti:", error.message);
  }
}

layaniSiti();