Memahami Async/Await di JavaScript Sampai ke Akarnya
Banyak developer JavaScript yang bisa pakai async/await tapi tidak bisa jelaskan kenapa kode tertentu berperilaku tidak seperti yang diharapkan. Ini karena mereka skip memahami fondasi di bawahnya.
JavaScript Adalah Single-Threaded
JavaScript hanya punya satu thread — artinya satu hal terjadi pada satu waktu. Tapi website bisa fetch data sambil animasi berjalan sambil user bisa scroll. Bagaimana?
Event Loop: Jantung Async JavaScript
Ada tiga komponen utama:
- Call Stack — tempat fungsi yang sedang dieksekusi
- Web APIs — tempat operasi async ditangani browser (setTimeout, fetch, event)
- Callback Queue — antrian callback yang menunggu dieksekusi
Event Loop tugasnya sederhana: kalau Call Stack kosong, ambil item pertama dari Queue dan masukkan ke Stack.
Dari Callback ke Promise ke Async/Await
Era Callback (masalah: callback hell)
getUserData(userId, function(user) {
getOrders(user.id, function(orders) {
getOrderDetails(orders[0].id, function(details) {
// Sudah 3 level — bayangkan kalau lebih dalam
});
});
});
Era Promise (lebih baik, tapi masih verbose)
getUserData(userId)
.then(user => getOrders(user.id))
.then(orders => getOrderDetails(orders[0].id))
.then(details => console.log(details))
.catch(error => console.error(error));
Era Async/Await (paling readable)
async function loadUserDetails(userId) {
try {
const user = await getUserData(userId);
const orders = await getOrders(user.id);
const details = await getOrderDetails(orders[0].id);
return details;
} catch (error) {
console.error("Gagal load data:", error);
}
}
Kesalahan Umum: Await Berurutan yang Tidak Perlu
// LAMBAT — request dijalankan berurutan (sequential)
// Total waktu: waktu request A + waktu request B
const userProfile = await fetchUserProfile(id);
const userPosts = await fetchUserPosts(id);
// CEPAT — request dijalankan bersamaan (parallel)
// Total waktu: waktu request yang paling lama
const [userProfile, userPosts] = await Promise.all([
fetchUserProfile(id),
fetchUserPosts(id)
]);
Promise.all vs Promise.allSettled vs Promise.race
// Promise.all — gagal jika salah satu request gagal
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// Promise.allSettled — tunggu semua selesai, meski ada yang gagal
const results = await Promise.allSettled([fetchA(), fetchB()]);
results.forEach(r => {
if (r.status === "fulfilled") console.log(r.value);
if (r.status === "rejected") console.error(r.reason);
});
// Promise.race — ambil hasil dari yang pertama selesai
const fastest = await Promise.race([fetchFromServer1(), fetchFromServer2()]);
Async/Await Bukan Pengganti Promise
Async/await adalah syntactic sugar di atas Promise. Di baliknya, semua masih Promise. Memahami Promise akan membuatmu jauh lebih efektif menggunakan async/await.