September 17, 2026

Event Loop, Callback, Promise, dan Async/Await.

Cara JavaScript menangani operasi asinkron lewat event loop, dari pola callback lama sampai async/await modern, beserta urutan eksekusinya.

JavaScript berjalan di satu thread tunggal, artinya hanya bisa mengerjakan satu hal dalam satu waktu. Namun aplikasi tetap bisa menangani banyak operasi yang butuh waktu lama (seperti mengambil data dari server) tanpa membekukan halaman, berkat mekanisme bernama event loop.

Call Stack dan Operasi Asinkron

console.log("Pertama");

setTimeout(() => {
  console.log("Ketiga");
}, 0);

console.log("Kedua");

// Output:
// Pertama
// Kedua
// Ketiga

Meski setTimeout diatur dengan delay 0 detik, callback-nya tetap dijalankan belakangan. Ini karena setTimeout menaruh callback-nya ke antrean terpisah, dan event loop baru mengambilnya setelah seluruh kode sinkron (call stack utama) selesai dijalankan.

Callback: Pola Asinkron Klasik

function ambilData(callback) {
  setTimeout(() => {
    callback({ nama: "Data dari server" });
  }, 1000);
}

ambilData((data) => {
  console.log(data.nama);
});

Pola callback jadi sulit dibaca saat beberapa operasi asinkron perlu dijalankan berurutan, menghasilkan struktur bersarang yang dikenal sebagai “callback hell”.

Promise: Memperbaiki Struktur Callback

function ambilData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const berhasil = true;
      if (berhasil) {
        resolve({ nama: "Data dari server" });
      } else {
        reject(new Error("Gagal mengambil data"));
      }
    }, 1000);
  });
}

ambilData()
  .then((data) => console.log(data.nama))
  .catch((error) => console.error(error.message));

Promise punya tiga kemungkinan status: pending (masih berjalan), fulfilled (berhasil), atau rejected (gagal), dan bisa dirangkai lewat .then() untuk menghindari struktur bersarang seperti pada callback.

async/await: Sintaks Paling Modern

async function tampilkanData() {
  try {
    const data = await ambilData();
    console.log(data.nama);
  } catch (error) {
    console.error(error.message);
  }
}

async/await sebenarnya adalah cara penulisan lain di atas Promise, dibuat agar kode asinkron terlihat dan terbaca seperti kode sinkron biasa dari atas ke bawah.

Microtask vs Macrotask

Event loop membedakan dua jenis antrean: microtask (Promise, queueMicrotask) dan macrotask (setTimeout, event DOM). Microtask selalu diproses lebih dulu sebelum macrotask berikutnya, meski keduanya sama-sama menunggu call stack kosong.

console.log("1");

setTimeout(() => console.log("2 (macrotask)"), 0);

Promise.resolve().then(() => console.log("3 (microtask)"));

console.log("4");

// Output: 1, 4, 3 (microtask), 2 (macrotask)

Menjalankan Beberapa Operasi Asinkron Sekaligus

async function ambilSemuaData() {
  const [profil, notifikasi] = await Promise.all([
    ambilProfil(),
    ambilNotifikasi(),
  ]);

  return { profil, notifikasi };
}

Promise.all() menjalankan beberapa Promise secara paralel dan menunggu semuanya selesai, jauh lebih cepat dibanding memakai await berurutan satu per satu jika operasi-operasi tersebut saling tidak bergantung.

Kesimpulan

Event loop memungkinkan JavaScript yang single-thread tetap responsif saat menunggu operasi yang butuh waktu lama, dengan callback sebagai pola paling dasar, Promise sebagai penyempurnaannya, dan async/await sebagai sintaks paling nyaman dibaca saat ini. Memahami urutan microtask dan macrotask juga penting untuk menjelaskan perilaku kode asinkron yang kadang terasa tidak intuitif di awal.

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.

Comments