async/await JavaScript: Menulis Kode Asinkron yang Terbaca seperti Kode Biasa

async/await adalah sintaks yang dibangun di atas promise, dan membuat kode asinkron bisa ditulis rapi dari atas ke bawah. Pelajari cara kerjanya, menangani error dengan try/catch, menjalankan tugas secara paralel, dan jebakan yang perlu dihindari.

Diterbitkan 11 Agustus 20269 menit bacaOleh ACY Partner Indonesia
async/await JavaScript — kode asinkron yang ditulis rapi dari atas ke bawah
300 × 250Slot Iklan TersediaPasang iklan Anda di sini

Promise sudah memberi kita cara yang rapi untuk menangani nilai yang belum siap — respons dari server, file dari disk, timer yang belum berbunyi. Tapi begitu kamu merangkai tiga atau empat .then() jadi satu, kodenya mulai melebar ke samping dan alur logikanya jadi lebih susah diikuti daripada seharusnya. Nah, async/await hadir untuk membereskan itu. Ini cuma lapisan sintaks tipis yang dibangun langsung di atas promise, dan membuat kamu bisa menulis kode asinkron dengan bentuk yang rata dari atas ke bawah — persis seperti menulis kode sinkron biasa — sementara di baliknya semuanya tetap non-blocking seperti sebelumnya.

Artikel ini mengasumsikan kamu sudah cukup paham soal promise. Kalau istilah “pending”, “resolved”, dan “rejected” masih terdengar asing, baca artikel itu dulu — async/await itu cuma “gula” di atas promise, bukan pengganti promise, dan jauh lebih masuk akal kalau kamu sudah tahu hal yang sedang disembunyikannya.

async/await itu sebenarnya apa

Satu ide yang perlu kamu pegang erat-erat: async/await tidak memperkenalkan cara baru untuk mengerjakan tugas asinkron. Ini cuma cara baru untuk menuliskan kode yang memakai promise. Di baliknya, kamu tetap berurusan dengan objek promise yang sama persis. Kedua kata kunci ini cuma membuat kamu berhenti menulis rangkaian .then() dan menggantinya dengan kode yang terbaca dari atas ke bawah.

Dua kata kunci yang mengerjakan semuanya:

  • async menandai sebuah fungsi. Fungsi async selalu mengembalikan promise, apa pun isi di dalamnya.
  • await menjeda fungsi async sampai sebuah promise selesai, lalu menyerahkan nilai hasilnya kepadamu.

Coba bandingkan logika yang sama yang ditulis dengan dua cara. Pertama dengan rangkaian promise biasa:

function ambilUser() {
  return fetch("/api/user")
    .then((response) => response.json())
    .then((user) => {
      console.log(user.name);
      return user;
    });
}

Sekarang dengan async/await:

async function ambilUser() {
  const response = await fetch("/api/user");
  const user = await response.json();
  console.log(user.name);
  return user;
}

Kedua fungsi melakukan hal yang sama persis dan sama-sama mengembalikan promise. Bedanya, yang kedua terbaca seperti kode biasa yang urut: ambil respons, parse, cetak namanya. Tanpa nesting, tanpa callback, tanpa rangkaian yang harus kamu telusuri dengan mata.

Kata kunci async

Menaruh async di depan sebuah fungsi mengubah satu hal yang konkret: fungsi itu sekarang selalu mengembalikan promise. Apa pun yang kamu return dari dalamnya akan jadi nilai yang di-resolve oleh promise itu.

async function sapa() {
  return "Halo";
}

sapa().then((pesan) => console.log(pesan)); // "Halo"

Walaupun isi fungsinya cuma mengembalikan string biasa, memanggil sapa() justru memberimu sebuah promise yang di-resolve menjadi "Halo" — itulah kenapa kamu bisa memanggil .then() di atasnya. Kalau kamu mengembalikan sebuah promise dari fungsi async, dia tidak akan terbungkus dua kali; JavaScript meratakannya otomatis, jadi yang kamu terima tetap satu promise saja.

Sebaliknya: kalau sebuah fungsi async melempar error, promise yang dikembalikan akan reject dengan error itu, bukan resolve. Fakta ini sebentar lagi kita pakai untuk menangani error.

Dari luar, fungsi async tetaplah promise

Gampang lupa bahwa pemanggil sebuah fungsi async menerima promise, bukan nilai akhirnya. sapa() tidak memberimu "Halo" — dia memberimu promise yang di-resolve menjadi "Halo". Untuk membukanya, kamu harus meng-await (di dalam fungsi async lain) atau menempelkan .then(). Tidak ada cara untuk “merogoh ke dalam” secara sinkron lalu mengambil nilainya, karena pada saat fungsi itu dipanggil, nilainya memang bisa jadi belum ada.

Kata kunci await

await adalah kata kunci yang nantinya paling sering kamu pakai. Taruh dia di depan promise mana pun, lalu dia melakukan dua hal: menjeda fungsi async di sekitarnya sampai promise itu selesai, lalu menghasilkan nilai yang di-resolve.

async function ambilHarga() {
  const response = await fetch("/api/product/1");
  const produk = await response.json();
  return produk.price;
}

Baca itu dari atas ke bawah. await pertama menunggu request jaringan dan menyimpan responsnya. await kedua menunggu body-nya selesai di-parse jadi JSON dan menyimpan objeknya. Tiap baris benar-benar menunggu baris di atasnya selesai dulu sebelum jalan. Di situlah daya tariknya: urutan yang kamu baca sama dengan urutan kejadiannya.

Bagian yang paling penting — dan yang sering bikin kaget orang yang datang dari bahasa lain — adalah bahwa await tidak memblokir sisa programmu. Selama sebuah fungsi async sedang dijeda di sebuah await, mesin JavaScript bebas menjalankan kode lain: menangani klik, menjalankan timer, menggambar ulang halaman. Yang dijeda cuma fungsi itu sendiri, bukan seluruh thread. Begitu promise-nya selesai, fungsi itu diam-diam melanjutkan dari titik terakhirnya.

await cuma bisa dipakai di dalam fungsi async

await hanya valid di dalam fungsi yang ditandai async (atau di level paling atas sebuah modul JavaScript). Kalau kamu menaruh await di fungsi biasa, kamu bakal kena syntax error. Jadi kedua kata kunci ini selalu jalan berdua: kapan pun kamu ingin meng-await sesuatu, fungsi yang menampungnya harus async. Satu pengecualian modern adalah top-level await di dalam modul ES, di mana kamu bisa langsung meng-await tanpa membungkusnya dalam fungsi — praktis, tapi cuma tersedia dalam konteks modul.

Menangani error dengan try/catch

Rangkaian promise menangani kegagalan dengan .catch(). Dengan async/await, kamu dapat sesuatu yang bahkan lebih akrab: blok try/catch biasa yang sudah kamu pakai untuk kode sinkron. Karena promise yang reject membuat await melempar error, kamu bisa membungkus await-await-mu dalam sebuah try dan menangkap kegagalan apa pun di satu tempat.

async function ambilUser() {
  try {
    const response = await fetch("/api/user");
    if (!response.ok) {
      throw new Error(`Request gagal: ${response.status}`);
    }
    const user = await response.json();
    return user;
  } catch (error) {
    console.error("Tidak bisa memuat user:", error.message);
    return null;
  }
}

Kalau fetch reject (misalnya jaringannya mati), atau kalau responsnya tidak OK dan kita melempar error secara manual, kontrol langsung lompat ke blok catch. Satu blok menangkap semua kegagalan dari seluruh await di dalam try — kamu tidak perlu handler terpisah untuk tiap langkah. Kalau kamu sudah baca artikel penanganan error, ini adalah try/catch yang sama yang sudah kamu kenal, sekarang menjangkau kode asinkron juga.

Rejection yang tak tertangani tidak crash dengan keras — tapi bukan berarti gratis

Kalau fungsi async melempar error dan tidak ada yang menangkapnya — tanpa try/catch di sekitarnya, tanpa .catch() pada promise yang dikembalikan — kamu akan mendapat unhandled promise rejection. Browser mencatat peringatan di console, dan di Node.js hal ini bisa menghentikan prosesnya tergantung versi dan pengaturannya. Bagian berbahayanya, di browser kegagalan ini sering terjadi diam-diam: bug-nya tidak meledak di depan matamu, dia cuma membuat sebuah fitur berhenti bekerja tanpa suara. Anggap setiap await yang bisa gagal sebagai tanggung jawabmu untuk ditangkap di suatu tempat sepanjang rantainya.

Menjalankan tugas secara paralel dengan Promise.all

Ini kesalahan performa yang paling umum saat memakai await, dan layak kamu tanamkan kuat-kuat di ingatan. Saat kamu menulis beberapa await berurutan, masing-masing menunggu yang sebelumnya selesai — jadi semuanya jalan berurutan:

async function muatDashboard() {
  const user = await fetch("/api/user");        // tunggu 1 detik
  const posts = await fetch("/api/posts");      // baru tunggu 1 detik lagi
  const stats = await fetch("/api/stats");      // baru tunggu 1 detik lagi
  // total: ~3 detik
}

Kalau ketiga request itu tidak saling bergantung, ini jelas pemborosan. Request kedua tidak butuh hasil yang pertama, jadi kenapa dia harus menunggu? Tanpa sadar kamu mengubah tiga tugas yang independen jadi antrean yang lambat.

Kalau tugasnya independen, jalankan semuanya sekaligus lalu await bareng dengan Promise.all:

async function muatDashboard() {
  const [user, posts, stats] = await Promise.all([
    fetch("/api/user"),
    fetch("/api/posts"),
    fetch("/api/stats"),
  ]);
  // total: ~1 detik (semuanya jalan bersamaan)
}

Promise.all menerima sebuah array berisi promise, menunggu semuanya resolve, lalu mengembalikan array hasilnya dalam urutan yang sama. Karena ketiga request dimulai bersamaan, bukan satu demi satu, total waktunya kira-kira selama yang paling lambat saja, bukan jumlah dari ketiganya. Pada dashboard nyata yang menarik data dari beberapa endpoint, ini bedanya antara terasa gesit dan terasa berat.

Berurutan kalau saling bergantung, paralel kalau independen

Patokannya: pakai await berurutan hanya kalau sebuah langkah memang butuh hasil langkah sebelumnya (kamu tidak bisa mengambil daftar pesanan seorang user sebelum tahu ID user-nya). Kalau tugasnya tidak saling bergantung, jalankan paralel dengan Promise.all. Satu catatan: Promise.all langsung reject begitu salah satu promise-nya reject. Kalau kamu lebih ingin menunggu semua tugas selesai lalu memeriksa hasil tiap-tiapnya secara terpisah — baik yang berhasil maupun yang gagal — pakai Promise.allSettled sebagai gantinya.

Contoh realistis

Coba kita gabungkan semuanya dengan sesuatu yang dekat dengan kode beneran: muat seorang user, lalu muat daftar pesanan user itu (yang memang bergantung pada user-nya), dan tangani kegagalan apa pun dengan rapi.

async function muatPesananUser(userId) {
  try {
    // Langkah 1 — ambil user-nya (datanya dibutuhkan lebih dulu)
    const userResponse = await fetch(`/api/users/${userId}`);
    if (!userResponse.ok) {
      throw new Error(`User tidak ditemukan (status ${userResponse.status})`);
    }
    const user = await userResponse.json();

    // Langkah 2 — dua ini tidak saling bergantung, jadi jalankan bareng
    const [pesanan, profil] = await Promise.all([
      fetch(`/api/users/${userId}/orders`).then((r) => r.json()),
      fetch(`/api/users/${userId}/profile`).then((r) => r.json()),
    ]);

    return { nama: user.name, pesanan, profil };
  } catch (error) {
    console.error("Gagal memuat pesanan user:", error.message);
    return null;
  }
}

// Memanggilnya — ingat, hasilnya adalah promise
muatPesananUser(42).then((data) => {
  if (data) {
    console.log(`${data.nama} punya ${data.pesanan.length} pesanan`);
  }
});

Perhatikan campuran yang disengaja di sini. Fetch user-nya dilakukan duluan dan sendirian, karena semua yang setelahnya butuh user itu. Setelah itu, fetch pesanan dan profil dijalankan paralel dengan Promise.all, karena keduanya tidak saling butuh. Satu try/catch menjaga semuanya. Dan si pemanggil memperlakukan nilai kembaliannya sebagai promise — karena memang itulah yang diberikan fungsi async. Pola ini — fetch, lalu bercabang ke paralel kalau bisa, lalu tangkap error sekali — sudah mencakup sebagian besar kode asinkron yang nantinya kamu tulis sungguhan.

Penutup

async/await adalah gaya yang paling banyak dipakai JavaScript modern, karena membuat logika asinkron jadi mudah dibaca tanpa mengorbankan sifat non-blocking di baliknya:

  • async/await adalah sintaks yang dibangun di atas promise — bukan mekanisme yang terpisah. Di baliknya kamu tetap bekerja dengan objek promise yang sama.
  • Fungsi async selalu mengembalikan promise. Apa pun yang kamu return jadi nilai hasilnya; apa pun yang kamu throw jadi rejection-nya.
  • await menjeda fungsi async sampai sebuah promise selesai lalu memberimu nilai hasilnya — tanpa memblokir sisa program. Dia cuma valid di dalam fungsi async (atau di level atas sebuah modul).
  • Tangani kegagalan dengan try/catch biasa, karena promise yang reject membuat await melempar error. Jangan biarkan rejection menganggur tanpa ditangani — dia bisa gagal diam-diam.
  • Jalankan tugas-tugas yang independen bareng dengan Promise.all, bukan di-await satu per satu. Berurutan kalau sebuah langkah bergantung pada yang sebelumnya; paralel kalau tidak.

Dengan promise dan async/await sudah kamu kuasai, kamu siap menuju tempat di mana keduanya paling sering kamu pakai: benar-benar berkomunikasi dengan server. Artikel berikutnya membahas Fetch API — cara meminta data dari sebuah URL, mengirim data balik, dan menangani responsnya — dengan persis pola async/await yang baru saja kamu pelajari di sini.

Tag:javascriptfrontendasync-awaitpromiseasinkronmenengah
728 × 90Slot Iklan TersediaPasang iklan Anda di sini

Artikel Terkait

Lihat Semua Artikel

Artikel yang Mungkin Kamu Suka

Apa Itu Framework Frontend? — chip kode dengan tag komponen
Frontend / Dasar

Apa Itu Framework Frontend?

Penjelasan ramah untuk pemula tentang apa itu framework frontend, kenapa mereka ada, masalah apa yang mereka selesaikan dibanding JavaScript biasa, dan kapan kamu memang sebaiknya memakainya.

20 Sep 20268 menit baca
Sampul fundamentals frontend bertuliskan Apa Itu Frontend dan HTML + CSS + JS
Frontend / Dasar

Apa Itu Frontend Development?

Panduan ramah pemula tentang frontend development: bagian website yang kamu lihat dan klik secara langsung. Pahami cara kerja HTML, CSS, dan JavaScript, serta bedanya frontend dengan backend.

20 Sep 20268 menit baca
Ilustrasi sampul untuk Apa yang Dikerjakan Frontend Developer
Frontend / Dasar

Apa yang Dikerjakan Frontend Developer: Panduan untuk Pemula

Penjelasan ramah pemula tentang apa yang sebenarnya dikerjakan frontend developer setiap hari: mengubah desain jadi antarmuka yang berfungsi, membangun UI yang bisa dipakai ulang, sampai urusan responsif, aksesibilitas, dan kecepatan.

20 Sep 20269 menit baca
Kartu judul bertuliskan Styling dan Interaktivitas di atas latar biru gelap ACY Partner
Frontend / Dasar

Cara Kerja Styling dan Interaktivitas

Penjelasan ramah pemula soal bagaimana CSS menyulap HTML polos jadi tampilan rapi, dan bagaimana JavaScript menambahkan perilaku — pola berpikir struktur, gaya, perilaku saat membangun halaman web.

20 Sep 20268 menit baca
Tiga lapisan front-end menyatu di satu halaman web
Frontend / Dasar

Cara HTML, CSS, dan JavaScript Bekerja Bersama

Panduan praktis untuk pemula: bikin satu tombol kecil, beri struktur dengan HTML, tampilan dengan CSS, dan perilaku dengan JavaScript, lalu lihat ketiga lapisan ini bekerja sama di halaman nyata.

20 Sep 20268 menit baca
Sampul ikhtisar praktik terbaik frontend
Frontend / Dasar

Ikhtisar Praktik Terbaik Frontend

Peta ringkas tentang apa itu frontend yang baik — HTML bermakna, CSS rapi, JavaScript secukupnya, desain responsif, performa, dan progressive enhancement, lengkap dengan arahan untuk mendalaminya.

20 Sep 20268 menit baca