Ilustrasi sampul artikel: Async Await Adalah: Cara Kerja, Contoh, dan Bedanya dengan Promise
·JavaScriptAsyncTutorial

Async Await Adalah: Cara Kerja, Contoh, dan Bedanya dengan Promise

Async await adalah cara menulis kode asynchronous JavaScript agar rapi seperti kode biasa. Pelajari cara kerjanya, perbedaannya dengan Promise & callback, plus contoh.

Pembuat Hyper Sheets & BelajarWeb.dev

Async/await adalah sintaks di JavaScript untuk menulis kode asynchronous (yang butuh waktu, seperti mengambil data dari internet) agar terlihat rapi dan mudah dibaca seperti kode biasa yang berjalan dari atas ke bawah. Kata kunci async menandai sebuah fungsi sebagai asinkron, sedangkan await menyuruh JavaScript “tunggu dulu sampai operasi ini selesai”, tapi tanpa membuat seluruh halaman membeku. Di balik layar, async/await sebenarnya hanyalah cara penulisan yang lebih manusiawi untuk sesuatu bernama Promise. Jadi keduanya bukan saingan; async/await dibangun di atas Promise.

Di panduan ini kamu akan memahami async/await benar-benar dari nol: apa arti “asynchronous”, kenapa JavaScript butuh konsep ini, cara pakai async dan await langkah demi langkah, cara menangani error dengan try/catch, sampai perbedaan jelas antara callback, Promise, dan async/await. Semua dengan contoh kode berbahasa Indonesia yang bisa langsung kamu coba di console browser.

Apa Itu Asynchronous?

Sebelum ke async/await, kita harus paham dulu satu fakta penting: JavaScript itu single-threaded, artinya ia hanya bisa mengerjakan satu hal dalam satu waktu, baris demi baris. Ini seperti seorang kasir tunggal di minimarket: ia melayani satu pelanggan sampai selesai, baru pindah ke pelanggan berikutnya.

Masalahnya muncul ketika ada operasi yang butuh waktu lama, misalnya:

  • Mengambil data dari server (API) lewat internet
  • Membaca file besar
  • Menunggu timer atau jeda waktu

Kalau JavaScript menunggu operasi lambat ini secara “sinkron” (blocking), seluruh halaman akan membeku, tombol tidak bisa diklik, animasi berhenti, teks tidak bisa di-scroll, sampai operasinya selesai. Bayangkan kasir tadi berhenti melayani semua orang karena satu pelanggan sedang menelepon temannya untuk minta izin belanja. Kacau, kan?

Di sinilah asynchronous (asinkron) berperan. Alih-alih menunggu sambil membekukan segalanya, JavaScript menitipkan operasi lambat itu “ke background”, lalu tetap lanjut mengerjakan kode lain. Begitu operasi background selesai, hasilnya baru diproses. Kasir tetap melayani pelanggan lain sementara yang menelepon minggir sebentar.

Kenapa Butuh Async/Await?

Dulu, cara menangani operasi asynchronous di JavaScript cukup berantakan. Kita akan lihat evolusinya sebentar lagi, tapi intinya: kode async yang lama sering jadi sulit dibaca, sulit di-debug, dan gampang salah. Async/await lahir pada tahun 2017 (ES2017) untuk menyelesaikan masalah ini, membuat kode asynchronous terlihat dan terbaca seperti kode sinkron biasa, padahal di dalamnya tetap non-blocking.

Coba lihat perbandingan singkat ini. Kode async yang mengambil data user, dengan gaya lama (Promise .then()):

fetch("https://api.example.com/user")
  .then((response) => response.json())
  .then((data) => {
    console.log(data.nama)
  })

Dan dengan async/await:

const response = await fetch("https://api.example.com/user")
const data = await response.json()
console.log(data.nama)

Versi async/await terbaca dari atas ke bawah, persis seperti kode biasa. Tidak ada rantai .then() yang menjorok ke kanan. Inilah alasan utama async/await begitu populer.

Cara Kerja Async dan Await

Ada dua kata kunci yang selalu berpasangan. Mari bedah satu per satu.

Kata Kunci async

Menaruh async di depan sebuah fungsi menandai bahwa fungsi itu asinkron. Efeknya: fungsi tersebut otomatis selalu mengembalikan sebuah Promise, dan, yang paling penting, di dalamnya kamu boleh memakai await.

// Fungsi biasa
function sapa() {
  return "Halo"
}

// Fungsi async
async function sapaAsync() {
  return "Halo"   // otomatis dibungkus jadi Promise
}

Kata Kunci await

await hanya boleh dipakai di dalam fungsi async. Artinya sederhana: “berhenti di baris ini, tunggu sampai Promise-nya selesai (resolve), lalu ambil nilainya dan lanjut ke baris berikutnya.” Yang penting, penungguan ini tidak memblokir halaman, kode di luar fungsi async ini tetap bisa jalan.

async function ambilUser() {
  console.log("Mulai mengambil data...")

  // await = tunggu fetch selesai, baru lanjut
  const response = await fetch("https://api.example.com/user")
  const data = await response.json()

  console.log("Data diterima:", data.nama)
  return data
}

Perhatikan bahwa tanpa await, variabel response tidak akan berisi data, melainkan sebuah Promise yang “masih dalam perjalanan”. Ini kesalahan pemula paling umum, dan kita bahas lagi nanti.

Urutan Eksekusi: Bagian yang Bikin Bingung

Konsep async paling sering membuat pemula pusing di satu hal: urutan kode tidak selalu berjalan dari atas ke bawah persis seperti yang terlihat. Baris yang sinkron jalan lebih dulu, sementara operasi yang di-await “menepi” sebentar sampai datanya siap.

Cara terbaik memahaminya adalah dengan melihatnya bergerak. Di demo bawah ini, coba jalankan kedua mode. Perhatikan mode “blocking” membekukan status halaman, sedangkan mode “async/await” menjaga halaman tetap responsif:

Coba Sendiri

Kode

console.log('1, Mulai')
const data = await ambilData() // 2 dtk
console.log('2, Data:', data)
console.log('3, Selesai')

Console (urutan asli)

Klik "Jalankan" untuk melihat urutannya…

Status halaman:● Responsif

Sudah lihat bedanya? Pada versi blocking, status halaman menjadi “BEKU” selama menunggu, pengguna tidak bisa berinteraksi sama sekali. Pada versi async/await, halaman tetap “Responsif” karena penungguan terjadi di background. Inilah inti kenapa aplikasi web modern nyaman dipakai: mereka tidak pernah membekukan layar hanya untuk menunggu data.

Menangani Error dengan try/catch

Operasi asynchronous sangat sering gagal, koneksi putus, server error, data tidak ditemukan. Dengan async/await, cara menangani error jadi sangat natural: bungkus saja dengan try/catch, sama seperti menangani error pada kode biasa.

async function ambilData() {
  try {
    const response = await fetch("https://api.example.com/user")

    // fetch tidak otomatis error untuk status 404/500, cek manual
    if (!response.ok) {
      throw new Error(`Server merespons: ${response.status}`)
    }

    const data = await response.json()
    return data
  } catch (error) {
    // Semua error di dalam try ditangkap di sini
    console.error("Gagal mengambil data:", error.message)
    return null
  } finally {
    // Selalu dijalankan, mau sukses atau gagal
    console.log("Proses selesai.")
  }
}

Blok try berisi kode yang berpotensi gagal, catch menangkap error apa pun yang terjadi di dalamnya, dan finally (opsional) selalu dijalankan di akhir. Pola ini jauh lebih rapi dibanding menangani error dengan .catch() di setiap rantai Promise.

Perbedaan Callback, Promise, dan Async/Await

Inilah bagian yang paling sering dicari: apa sebenarnya bedanya? Ketiganya menyelesaikan masalah yang sama (menangani operasi asynchronous), tapi lahir di generasi yang berbeda. Async/await adalah yang termuda dan paling nyaman.

Aspek Callback Promise Async/Await
Muncul sejak Awal JavaScript ES2015 ES2017
Keterbacaan Buruk (menjorok ke kanan) Sedang (rantai .then()) Terbaik (seperti kode biasa)
Error handling if (err) manual di tiap callback .catch() try/catch
Masalah utama “Callback hell” Rantai panjang Hampir tidak ada

Mari lihat kode yang sama ditulis dengan ketiga gaya.

1. Callback (gaya lama): fungsi dilempar sebagai argumen, dijalankan saat data siap. Kalau butuh beberapa langkah berurutan, callback saling bersarang dan membentuk “callback hell” yang menjorok ke kanan:

ambilUser(function (user) {
  ambilPostingan(user.id, function (postingan) {
    ambilKomentar(postingan.id, function (komentar) {
      console.log(komentar)   // menjorok makin dalam...
    })
  })
})

2. Promise (.then()): merapikan callback hell menjadi rantai yang lebih datar. Sebuah Promise adalah “janji” bahwa sebuah nilai akan tersedia di masa depan, entah berhasil (resolved) atau gagal (rejected):

ambilUser()
  .then((user) => ambilPostingan(user.id))
  .then((postingan) => ambilKomentar(postingan.id))
  .then((komentar) => console.log(komentar))
  .catch((error) => console.error(error))

3. Async/Await: menulis logika yang sama seolah-olah sinkron. Paling mudah dibaca dan di-debug:

async function tampilkanKomentar() {
  try {
    const user = await ambilUser()
    const postingan = await ambilPostingan(user.id)
    const komentar = await ambilKomentar(postingan.id)
    console.log(komentar)
  } catch (error) {
    console.error(error)
  }
}

Menjalankan Beberapa Tugas Sekaligus dengan Promise.all

Satu jebakan performa: kalau kamu meng-await beberapa operasi yang tidak saling bergantung secara berurutan, kamu membuatnya menunggu satu per satu tanpa alasan.

// ❌ Lambat: menunggu satu per satu (total ~3 detik)
const user = await ambilUser()        // 1 detik
const produk = await ambilProduk()    // 1 detik
const berita = await ambilBerita()    // 1 detik

Kalau ketiga data tidak saling butuh, jalankan bersamaan dengan Promise.all. Ia menunggu semua Promise selesai secara paralel:

// ✅ Cepat: jalan bersamaan (total ~1 detik)
const [user, produk, berita] = await Promise.all([
  ambilUser(),
  ambilProduk(),
  ambilBerita(),
])

Gunakan await berurutan hanya jika langkah berikutnya membutuhkan hasil langkah sebelumnya (misal butuh user.id untuk mengambil postingan). Kalau tidak, Promise.all jauh lebih cepat.

Contoh Nyata: Tombol dengan Loading State

Mari gabungkan semuanya dalam pola yang akan kamu pakai di aplikasi sungguhan, sebuah tombol yang mengambil data dan menampilkan status loading:

const tombol = document.getElementById("tombol-muat")
const hasil = document.getElementById("hasil")

tombol.addEventListener("click", async () => {
  tombol.disabled = true
  hasil.textContent = "Memuat..."

  try {
    const response = await fetch("https://api.example.com/kutipan")
    if (!response.ok) throw new Error("Gagal memuat")

    const data = await response.json()
    hasil.textContent = data.kutipan
  } catch (error) {
    hasil.textContent = "Terjadi kesalahan. Coba lagi."
  } finally {
    tombol.disabled = false
  }
})

Perhatikan async langsung ditaruh di depan fungsi callback event listener. Selama data dimuat, tombol dinonaktifkan dan teks “Memuat…” muncul, persis pengalaman yang kamu rasakan di aplikasi modern. Karena async, seluruh halaman tetap bisa di-scroll dan diklik selama menunggu.

Kalau kamu ingin melihat urutan eksekusi async/await langkah demi langkah secara interaktif, pelajaran async/await di Hyper Sheets menjelaskannya dengan latihan yang bisa langsung dikerjakan di browser tanpa perlu setup apa pun.

Kesalahan Umum Pemula

Beberapa jebakan yang hampir selalu dialami saat pertama kali belajar async/await:

1. Lupa await. Memanggil fungsi async tanpa await membuatmu memegang Promise, bukan datanya. Hasilnya sering muncul [object Promise] atau undefined.

const data = ambilData()          // ❌ data = Promise, bukan hasilnya
const data = await ambilData()    // ✅ data = hasil sebenarnya

2. Menulis await di luar fungsi async. Ini menghasilkan SyntaxError. Pastikan fungsi pembungkusnya diberi async.

3. Lupa try/catch. Kalau Promise yang di-await gagal dan tidak ada try/catch, error-nya menjadi unhandled rejection yang bisa membuat aplikasi berperilaku aneh.

4. await berurutan padahal bisa paralel. Seperti dibahas di atas, pakai Promise.all untuk operasi yang tidak saling bergantung.

5. Mengira async membuat kode berjalan di thread lain. Tidak. JavaScript tetap single-threaded; async hanya mengatur kapan kode dijalankan, bukan menjalankannya di thread paralel.

Konsep async ini adalah salah satu bagian tersulit sekaligus terpenting dalam JavaScript. Kalau fondasi bahasanya masih goyah, ada baiknya mengulang dulu panduan JavaScript dari nol yang membahas variabel, fungsi, dan event sebelum menyelam lebih dalam ke async.

Cek Pemahaman

Apa yang terjadi jika kamu menulis "const data = fetch(url)" tanpa await (di dalam fungsi async)?

FAQ Seputar Async/Await

Apa itu async/await dengan bahasa sederhana?

Async/await adalah cara menulis kode JavaScript yang butuh menunggu sesuatu (seperti mengambil data dari internet) supaya kodenya rapi dan mudah dibaca. async menandai fungsi sebagai “boleh menunggu”, dan await berarti “tunggu operasi ini selesai dulu, baru lanjut”, tapi tanpa membekukan halaman. Ibaratnya kamu memesan makanan lewat aplikasi: kamu menunggu pesanan datang, tapi tetap bisa melakukan hal lain sambil menunggu.

Apa perbedaan async/await dan Promise?

Keduanya bukan saingan, async/await sebenarnya dibangun di atas Promise. Promise adalah objek yang mewakili “janji” sebuah nilai di masa depan, biasanya ditangani dengan rantai .then(). Async/await adalah syntactic sugar (cara penulisan yang lebih manis) untuk Promise yang sama, sehingga kodenya terbaca seperti kode sinkron biasa dari atas ke bawah. Setiap fungsi async selalu mengembalikan sebuah Promise.

Apakah await membuat halaman membeku?

Tidak. Ini kesalahpahaman paling umum. await memang “menunggu”, tapi penungguannya terjadi di background sehingga sisa halaman tetap responsif, pengguna masih bisa scroll dan klik. Yang membekukan halaman justru operasi synchronous yang lambat, bukan async/await.

Kapan sebaiknya pakai Promise.all daripada await biasa?

Gunakan Promise.all ketika kamu punya beberapa operasi async yang tidak saling bergantung dan ingin menjalankannya bersamaan agar lebih cepat. Kalau kamu meng-await satu per satu, total waktunya menjumlah. Dengan Promise.all, semuanya jalan paralel dan total waktunya kira-kira sama dengan operasi terlama. Tetap pakai await berurutan jika langkah berikutnya butuh hasil langkah sebelumnya.

Apakah harus belajar Promise dulu sebelum async/await?

Sangat disarankan memahami konsep dasar Promise (resolved, rejected, .then, .catch) walau tidak harus mendalam. Karena async/await hanyalah cara penulisan Promise, memahami apa yang terjadi di balik layar akan sangat membantu saat debugging dan saat menghadapi kasus yang butuh Promise.all atau Promise.race. Untuk kode sehari-hari sendiri, async/await bisa langsung kamu pakai.

Kenapa dapat error “await is only valid in async functions”?

Karena kamu menulis await di sebuah fungsi yang belum ditandai async. Solusinya: tambahkan kata kunci async di depan fungsi yang berisi await tersebut. Error ini adalah salah satu yang paling sering dialami pemula dan biasanya cepat teratasi begitu kamu menambahkan async.

Setelah Async/Await, Belajar Apa?

Setelah nyaman dengan async/await, berikut jalur lanjutan yang masuk akal:

  1. fetch dan REST API, hampir semua penggunaan async/await di dunia nyata melibatkan pengambilan data dari API. Latih pola try/catch + response.ok sampai jadi refleks.
  2. Promise.all, Promise.race, Promise.allSettled, untuk mengatur banyak operasi async sekaligus dengan strategi berbeda.
  3. React dan data fetching, di React, async/await sering dipakai di dalam useEffect atau lewat library seperti TanStack Query. Kalau kamu belajar React, mulai dari belajar React dari nol.
  4. Error handling lanjutan, membuat pesan error yang ramah pengguna dan mekanisme retry otomatis.

Async/await adalah salah satu keterampilan yang membedakan pemula dari developer yang siap membangun aplikasi sungguhan. Setiap aplikasi web modern, dari toko online sampai media sosial, bergantung pada kemampuan mengambil dan menampilkan data tanpa membekukan layar. Kuasai konsepnya pelan-pelan, banyak praktik dengan API sungguhan, dan jangan takut membaca pesan error karena di situlah proses belajar yang sesungguhnya terjadi. Kalau kamu masih ingin mengulang fondasi dasarnya, panduan JavaScript untuk pemula adalah tempat yang tepat untuk kembali.