Pernah nunggu lama banget saat website kamu ngeload data? Atau ngerasa aplikasi yang kamu buat lemot kayak siput? 🤔 Nah, ada satu rahasia yang bisa bikin web app kamu jauh lebih cepat dan responsif—yup, itu dia: Asynchronous JavaScript! 🎉
Gak perlu pusing dengan istilah yang ribet atau kode yang bikin bingung. Di artikel ini, kita bakal bahas asynchronous JavaScript dengan cara yang super gampang.
Kamu bakal belajar kenapa teknik ini penting, cara menghindari jebakan atau kesalahan umum, dan bagaimana bikin kode yang cepat tanpa bikin pusing.
Jadi, siap untuk upgrade skill coding kamu dan bikin web app anti ngelag?
Table of contents
Open Table of contents
Sinkron vs Asinkron: Apa Bedanya?
Oke, sebelum kita lebih jauh bahas soal asynchronous, kita harus paham dulu bedanya synchronous dan asynchronous. Ini dasar banget, tapi penting supaya kamu gak bingung ke depannya.
Apa Itu Synchronous?
Bayangin kamu lagi di kasir supermarket, dan ada satu orang yang lagi antri di depan kamu. Kamu harus nunggu sampai orang itu selesai bayar dulu sebelum kamu bisa maju.
Nah, ini yang namanya synchronous. Dalam konteks JavaScript, kode synchronous dieksekusi satu per satu, dari atas ke bawah. Jadi, kalau ada satu tugas yang makan waktu lama, kode setelahnya harus nunggu sampai tugas itu selesai.
Contoh kode synchronous:
console.log("Mulai antrian...");
function bayar() {
for (let i = 0; i < 1000000000; i++) {} // Simulasi proses lama
console.log("Pembayaran selesai");
}
bayar();
console.log("Lanjut antrian...");
Hasil yang keluar di console:
Mulai antrian...
Pembayaran selesai
Lanjut antrian...
Di sini, setelah kita panggil fungsi bayar(), kode lain harus nunggu sampai proses pembayaran selesai sebelum bisa lanjut. Kalau prosesnya lama, ya halaman bisa jadi lemot atau freeze.
Apa Itu Asynchronous?
Sekarang, bayangin skenario lain. Kamu pesen kopi di tempat yang sibuk, tapi kali ini barista ngasih nomor antrian ke kamu. Sambil nunggu kopi jadi, kamu bisa duduk santai, ngobrol sama teman, atau scrolling medsos. Ketika kopi udah siap, barista manggil nomor kamu. Ini adalah konsep Asinkron.
Nah, itu yang namanya asynchronous. Kode bisa “mengerjakan hal lain” sambil nunggu tugas yang butuh waktu lama. Dengan cara ini, JavaScript gak harus “macet” nunggu satu tugas selesai sebelum bisa lanjut ke yang lain.
Contoh kode asynchronous (menggunakan setTimeout untuk simulasi):
console.log("Mulai antrian...");
function bayar() {
setTimeout(() => {
console.log("Pembayaran selesai");
}, 3000); // Simulasi delay 3 detik
}
bayar();
console.log("Lanjut antrian...");
Hasil yang keluar di console:
Mulai antrian...
Lanjut antrian...
Pembayaran selesai
Perhatikan, setelah fungsi bayar() dipanggil, program gak nunggu dan langsung lanjut ke perintah berikutnya (Lanjut antrian...). Sementara itu, pembayaran masih berjalan di belakang layar dan baru selesai beberapa detik kemudian.
Kenapa harus belajar asynchronous?
- Biar kode gak nge-freeze: kalau gak pakai asynchronous, kode kamu bakal nunggu proses lama selesai dulu sebelum bisa ngerjain hal lain. Ini bisa bikin website kayak “macet” alias nge-freeze.
- Pengalaman pengguna yang lebih baik: pengunjung gak akan merasa terganggu karena halaman yang lemot. Mereka bisa tetap browsing atau interaksi sama halaman lain.
- Penting buat proyek nyata: hampir semua aplikasi web modern pakai asynchronous, terutama pas berhubungan dengan server, seperti ngambil data API. Kalau kamu mau jadi web developer handal, ini wajib dikuasai.
- SEO: website yang cepat dan responsif gak cuma bikin pengunjung happy, tapi juga disayang sama Google. Kecepatan loading halaman bisa ngefek ke peringkat SEO, lho!
Jadi, ngerti bedanya synchronous dan asynchronous itu wajib banget.
Asynchronous bikin website kamu lebih responsif dan user-friendly, sementara synchronous lebih cocok buat tugas-tugas kecil dan cepat.
Callback: Cara Lama yang Masih Dipakai
Sekarang kita udah ngerti bedanya synchronous dan asynchronous, mari kita kenalan dengan salah satu cara paling awal buat ngelakuin tugas secara asynchronous di JavaScript: Callback.
Apa Itu Callback?
Bayangin kamu lagi nunggu temen yang janji mau dateng ke rumah. Daripada nungguin di depan pintu, kamu bilang, “Kalau udah nyampe, kasih tahu aku ya.”
Nah, ketika temenmu sampai, dia akan ngasih tahu kamu (misalnya lewat telepon atau chat), dan baru deh kamu bisa lanjut ngelakuin sesuatu, seperti bukain pintu.
Di JavaScript, callback itu semacam “pemberitahuan” buat kode kita. Jadi, ketika ada tugas yang selesai (misalnya ngambil data dari server), kita bisa kasih tahu fungsi tertentu buat dijalankan setelahnya.
Contoh Sederhana Callback
Misalnya kita punya fungsi buat ngambil data dari server. Karena ngambil data bisa makan waktu, kita gak mau kode kita nge-freeze nungguin proses ini selesai. Jadi, kita kasih callback supaya kita bisa ngelakuin hal lain dulu, dan baru nanti eksekusi kode ketika datanya udah siap.
function ambilData(callback) {
setTimeout(() => {
console.log("Data berhasil diambil");
callback(); // Panggil callback setelah data diambil
}, 2000); // Simulasi delay 2 detik
}
function tampilkanData() {
console.log("Tampilkan data di layar");
}
console.log("Mulai ambil data...");
ambilData(tampilkanData);
Output di console:
Mulai ambil data...
Data berhasil diambil
Tampilkan data di layar
Di sini, ambilData() adalah fungsi yang mengambil data (dengan delay 2 detik), dan setelah data berhasil diambil, dia memanggil callback() yaitu fungsi tampilkanData(). Jadi, kita bisa nunggu data sambil tetap ngelakuin hal lain.
Kelemahan Callback: Callback Hell
Meskipun callback berguna, dia punya satu kelemahan besar yang sering bikin pusing, terutama kalau tugas kita mulai kompleks.
Coba bayangin kalau kita harus ngelakuin beberapa tugas berurutan, misalnya ambil data, olah data, simpan data, lalu tampilkan hasilnya. Dengan callback, kodenya bisa jadi berlapis-lapis seperti ini:
ambilData(data => {
olahData(data, hasilOlah => {
simpanData(hasilOlah, respon => {
tampilkanHasil(respon, () => {
console.log("Semua tugas selesai!");
});
});
});
});
Kode di atas udah mulai keliatan rumit, kan? Ini yang sering disebut callback hell—kondisi di mana kode kita jadi berlapis-lapis, sulit dibaca, dan susah di-maintain.
Ini salah satu alasan kenapa callback mulai ditinggalkan untuk tugas-tugas yang kompleks, meskipun dia masih digunakan untuk hal-hal sederhana.
Promises: Jalan Tengah dari Callback Hell
Setelah tau tentang callback dan masalah yang bisa muncul seperti callback hell, sekarang saatnya kita kenalan sama solusi yang lebih elegan dan modern di JavaScript, yaitu Promises.
Apa Itu Promises?
Bayangin kamu lagi pesan makanan lewat aplikasi ojek online. Setelah pesan, kamu gak langsung dapet makanan kan? Kamu harus nunggu sampai makanan diantar. Nah, di aplikasi biasanya ada status pesanan yang nunjukin apakah makananmu udah dibikin, sedang diantar, atau udah sampai.
Ini mirip dengan konsep Promises di JavaScript. Promises itu adalah objek yang mewakili hasil dari suatu operasi asynchronous yang mungkin selesai sekarang, nanti, atau gak sama sekali.
Ada tiga status dalam promises:
- Pending: Promises masih dalam proses, belum selesai.
- Fulfilled: Promises berhasil diselesaikan, dan kita punya hasilnya.
- Rejected: Promises gagal diselesaikan, ada error yang terjadi.
Mari kita lihat gimana cara kerja promises dengan contoh sederhana. Misalnya kita punya fungsi yang ngambil data dari server, dan fungsi ini balikin promises.
function ambilData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const sukses = true; // Ubah jadi false untuk simulasi error
if (sukses) {
resolve("Data berhasil diambil");
} else {
reject("Gagal mengambil data");
}
}, 2000); // Simulasi delay 2 detik
});
}
ambilData()
.then(hasil => {
console.log(hasil); // "Data berhasil diambil"
})
.catch(error => {
console.log(error); // "Gagal mengambil data"
});
Penjelasan:
- resolve adalah fungsi yang dipanggil ketika promises selesai dengan sukses.
- reject adalah fungsi yang dipanggil ketika terjadi error.
- .then() digunakan untuk menangkap hasil jika promises berhasil diselesaikan (fulfilled).
- .catch() digunakan untuk menangani error jika promises gagal (rejected).
Kenapa Promises Lebih Baik?
- Lebih rapi dan mudah dibaca: Dibandingkan dengan callback yang bisa berlapis-lapis, promises bikin kode lebih rapi. Kita bisa chain
.then()dan.catch()buat nangani hasil dan error. - Lebih kuat untuk tugas berantai: Kalau ada beberapa tugas asynchronous yang harus dijalankan berurutan, promises jauh lebih mudah dikelola dibanding callback.
- Error handling yang lebih baik: Dengan
.catch(), kita bisa dengan mudah menangani semua error yang terjadi dalam chain promises, tanpa harus nambah callback di setiap langkah.
Contoh Penggunaan Promises
Bayangin kita lagi ngebuat aplikasi yang ambil data user, olah datanya, dan simpan ke database, semuanya secara asynchronous. Dengan promises, kode kita bisa jadi seperti ini:
ambilDataUser()
.then(dataUser => {
return olahData(dataUser);
})
.then(hasilOlah => {
return simpanKeDatabase(hasilOlah);
})
.then(respon => {
console.log("Semua proses selesai:", respon);
})
.catch(error => {
console.log("Terjadi kesalahan:", error);
});
Dengan promises, kita bisa dengan mudah urutkan tugas-tugas kita tanpa bikin kode jadi berlapis. Kalau ada error di salah satu step, kita bisa langsung tangani dengan .catch() di akhir chain.
Async/Await: Cara Modern dan Lebih Sederhana
Sekarang kita masuk ke cara paling modern dan user-friendly dalam menangani asynchronous di JavaScript, yaitu Async/Await. Kalau kamu udah ngerti konsep Promises, maka Async/Await bakal terasa jauh lebih mudah dan intuitif.
Apa Itu Async/Await?
Async/Await di JavaScript itu unik, karena dia bikin kode asynchronous keliatan seperti synchronous, lebih sederhana dan mudah dibaca.
Async adalah keyword yang kita tambahin di depan fungsi untuk mengubahnya menjadi fungsi asynchronous. Artinya, fungsi tersebut akan selalu mengembalikan promises.
Await adalah keyword yang digunakan di dalam fungsi async untuk menunggu hasil dari promises sebelum melanjutkan eksekusi kode berikutnya.
Kita bakal ambil contoh dari promises sebelumnya, tapi kali ini kita tulis ulang pake Async/Await.
async function ambilData() {
try {
const hasil = await new Promise((resolve, reject) => {
setTimeout(() => {
const sukses = true; // Ubah jadi false untuk simulasi error
if (sukses) {
resolve("Data berhasil diambil");
} else {
reject("Gagal mengambil data");
}
}, 2000);
});
console.log(hasil); // "Data berhasil diambil"
} catch (error) {
console.log(error); // "Gagal mengambil data"
}
}
ambilData();
Penjelasan:
- async function ambilData(): Dengan menambahkan
asyncdi depan fungsi, kita ubah fungsi ini jadi asynchronous. - await: Keyword ini digunakan buat nunggu hasil dari promises. Dengan
await, kita bisa menulis kode seolah-olah semuanya berjalan secara synchronous. - try/catch: Digunakan untuk menangani error, sama seperti
.catch()di promises.
Kenapa Async/Await Lebih Disukai?
- Kode yang lebih bersih dan mudah dibaca: Async/Await bikin kode kita jadi mirip seperti synchronous, lebih gampang dipahami dan di-maintain.
- Error handling yang sederhana: dengan
try/catch, kita bisa nangani error secara lebih natural, seperti halnya kode synchronous. - Pengganti yang lebih kuat untuk callback dan promises: Async/Await menggabungkan kekuatan promises tapi dengan sintaks yang lebih sederhana dan bersih.
Contoh Penggunaan Async/Await
Kita ambil contoh dari kode promises sebelumnya, tapi kali ini dengan menggunakan Async/Await:
async function prosesDataUser() {
try {
const dataUser = await ambilDataUser();
const hasilOlah = await olahData(dataUser);
const respon = await simpanKeDatabase(hasilOlah);
console.log("Semua proses selesai:", respon);
} catch (error) {
console.log("Terjadi kesalahan:", error);
}
}
prosesDataUser();
Di sini, await bikin kode kita berjalan step-by-step, seperti synchronous, tanpa perlu nge-chain .then() dan .catch() seperti di promises. Ini bikin kode jauh lebih rapi dan gampang di-maintain.
Async/Await adalah cara paling modern dan direkomendasikan buat menangani tugas asynchronous di JavaScript.
Dengan Async/Await, kamu bisa menulis kode asynchronous yang rapi, mudah dibaca, dan gampang di-maintain, tanpa kehilangan fleksibilitas yang ditawarkan oleh promises.
Ini adalah skill yang wajib dikuasai kalau kamu serius belajar JavaScript, apalagi kalau mau bikin aplikasi web yang kompleks.
Studi Kasus: Membuat Permintaan API dengan Fetch
Setelah kita memahami konsep dasar asynchronous, callback, promises, dan async/await, sekarang saatnya melihat bagaimana semua ini diterapkan dalam dunia nyata. Salah satu contoh paling umum adalah membuat permintaan API menggunakan fetch.
Apa Itu Fetch?
Fetch adalah API bawaan di JavaScript yang digunakan untuk mengambil data dari server, baik itu dalam bentuk JSON, teks, atau bahkan file.
Fetch bekerja secara asynchronous, jadi ketika kita memanggil fetch, kita bisa terus menjalankan kode lainnya tanpa harus menunggu respons dari server.
Fetch ini menggantikan XMLHttpRequest (XHR) yang dulu sering dipakai. Sintaksnya lebih sederhana dan lebih mudah dipahami, terutama kalau digabungkan dengan promises atau async/await.
Contoh Penggunaan Fetch dengan Promises
Pertama, mari kita lihat gimana cara menggunakan fetch dengan promises. Misalnya, kita mau ngambil data pengguna dari sebuah API.
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
if (!response.ok) {
throw new Error("Gagal mengambil data");
}
return response.json();
})
.then(data => {
console.log("Data pengguna:", data);
})
.catch(error => {
console.error("Terjadi kesalahan:", error);
});
Penjelasan:
- fetch(‘url’): Memulai permintaan ke URL yang kita tentukan.
- response.ok: Mengecek apakah permintaan berhasil atau tidak.
- response.json(): Mengambil respons dalam format JSON.
- .then(): Menangani hasil dari promises fetch.
- .catch(): Menangani error yang terjadi selama proses fetch.
Contoh Penggunaan Fetch dengan Async/Await
Sekarang kita coba ulang kode di atas, tapi menggunakan async/await biar lebih sederhana dan rapi.
async function ambilDataPengguna() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error("Gagal mengambil data");
}
const data = await response.json();
console.log("Data pengguna:", data);
} catch (error) {
console.error("Terjadi kesalahan:", error);
}
}
ambilDataPengguna();
Penjelasan:
- async function ambilDataPengguna(): Kita buat fungsi async yang akan menangani proses fetch.
- await fetch(‘url’): Kita menunggu hingga fetch selesai dan mendapatkan respons.
- await response.json(): Menunggu hingga JSON selesai diparsing.
- try/catch: Digunakan untuk menangani error jika fetch gagal.
Menampilkan Daftar Pengguna
Sekarang, kita coba buat contoh aplikasi sederhana yang ngambil data pengguna dari API dan menampilkannya di halaman web.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Daftar Pengguna</title>
</head>
<body>
<h1>Daftar Pengguna</h1>
<ul id="user-list"></ul>
<script>
async function ambilDataPengguna() {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
if (!response.ok) {
throw new Error("Gagal mengambil data");
}
const data = await response.json();
const userList = document.getElementById("user-list");
data.forEach(user => {
const li = document.createElement("li");
li.textContent = `${user.name} (${user.email})`;
userList.appendChild(li);
});
} catch (error) {
console.error("Terjadi kesalahan:", error);
}
}
ambilDataPengguna();
</script>
</body>
</html>
Di sini, kita memanfaatkan fetch dan async/await untuk ngambil data dari API, lalu menampilkan daftar pengguna ke halaman web dalam bentuk list.
Fetch adalah cara yang efektif dan modern untuk mengambil data dari server, dan ketika digabungkan dengan Async/Await, hasilnya adalah kode yang rapi, mudah dibaca, dan efisien.
Kesalahan Umum Saat Menggunakan Asynchronous JavaScript
Nah, buat kamu yang baru belajar asynchronous, penting buat tau kesalahan-kesalahan umum yang sering terjadi. Yuk, kita bahas beberapa kesalahan yang sering bikin kepala pusing dan cara menghindarinya!
1. Mengabaikan Error Handling
Salah satu kesalahan terbesar adalah lupa menangani error. Bayangkan kamu lagi pesan makanan lewat aplikasi, tapi aplikasi-nya nggak nunjukin pesan kesalahan kalau orderan gagal.
Tentu aja bikin frustrasi! Di JavaScript, kalau kamu lupa tangani error, aplikasi bisa berhenti bekerja tanpa pemberitahuan yang jelas.
Contoh kesalahan:
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(data => console.log(data)); // Error di sini nggak tertangkap!
Solusi:
Selalu sertakan .catch() di akhir promises atau gunakan try/catch di async/await untuk menangani error.
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
if (!response.ok) {
throw new Error("Gagal mengambil data");
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error("Terjadi kesalahan:", error));
2. Tidak Memahami Urutan Eksekusi
Asynchronous code itu berjalan di luar urutan normal kode kamu. Ini bisa bikin bingung, terutama kalau kamu expect hasilnya langsung keluar.
Contoh:
function ambilData() {
setTimeout(() => {
console.log("Data berhasil diambil");
}, 1000);
}
console.log("Sebelum ambil data");
ambilData();
console.log("Setelah ambil data");
Outputnya bakal seperti ini:
Sebelum ambil data
Setelah ambil data
Data berhasil diambil
Ini karena setTimeout berjalan di luar urutan normal, jadi pastikan kamu paham kalau kode asynchronous tidak selalu jalan sesuai urutan.
3. Terlalu Banyak Callback (Callback Hell)
Kalau kamu terlalu banyak nesting callback, kode bisa jadi sangat susah dibaca dan di-debug. Ini sering disebut sebagai callback hell.
Contoh kesalahan:
function ambilData(callback) {
setTimeout(() => {
callback(null, "Data A");
}, 1000);
}
function prosesData(data, callback) {
setTimeout(() => {
callback(null, `Proses ${data}`);
}, 1000);
}
ambilData((error, data) => {
if (error) {
console.error(error);
return;
}
prosesData(data, (error, hasil) => {
if (error) {
console.error(error);
return;
}
console.log(hasil);
});
});
Solusi:
Pakai Promises atau Async/Await untuk merapikan kode kamu dan menghindari callback hell.
4. Mengabaikan Pembatalan Request
Kadang, kamu mungkin perlu membatalkan request yang sudah dikirim, misalnya saat user berpindah halaman atau membatalkan input.
Contoh kesalahan:
let controller = new AbortController();
fetch("https://jsonplaceholder.typicode.com/users", {
signal: controller.signal,
})
.then(response => response.json())
.then(data => console.log(data));
// Harusnya ada mekanisme untuk membatalkan request di sini
Solusi:
Gunakan AbortController untuk membatalkan request jika diperlukan.
let controller = new AbortController();
fetch("https://jsonplaceholder.typicode.com/users", {
signal: controller.signal,
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === "AbortError") {
console.log("Request dibatalkan");
} else {
console.error("Terjadi kesalahan:", error);
}
});
// Contoh pembatalan
controller.abort();
5. Salah Paham Tentang Status Promises
Kadang, orang bingung antara status pending, fulfilled, dan rejected pada promises. Penting untuk paham status ini biar bisa menangani hasil dengan benar.
Contoh kesalahan:
const promise = new Promise((resolve, reject) => {
// Resolve atau reject disini
});
promise
.then(result => {
// Menangani fulfilled
})
.catch(error => {
// Menangani rejected
});
Solusi:
Pahami dengan baik status promises dan gunakan .then() dan .catch() dengan benar untuk menangani hasil dan error.
6. Tidak Menggunakan Promise.all untuk Operasi Paralel
Saat kamu punya beberapa operasi asynchronous yang bisa berjalan bersamaan, nggak perlu menunggu satu per satu selesai. Menggunakan Promise.all bisa menghemat waktu dan membuat aplikasi kamu lebih efisien.
Contoh kesalahan:
async function loadData() {
const data1 = await fetch("url1");
const data2 = await fetch("url2");
const data3 = await fetch("url3");
console.log(data1, data2, data3);
}
Solusi:
async function loadData() {
const [data1, data2, data3] = await Promise.all([
fetch("url1"),
fetch("url2"),
fetch("url3"),
]);
console.log(data1, data2, data3);
}
Menangani asynchronous JavaScript memang butuh latihan dan perhatian khusus. Dengan memahami kesalahan-kesalahan umum ini dan cara menghindarinya, kamu bisa bikin aplikasi web yang lebih stabil dan user-friendly.
Kesimpulan
Asynchronous JavaScript memungkinkan aplikasi web untuk berjalan lebih responsif dan efisien. Dengan menggunakan teknik asynchronous, kamu bisa melakukan beberapa tugas sekaligus tanpa harus menunggu salah satu tugas selesai sebelum memulai yang lainnya.
Ini sangat berguna, terutama saat kamu bekerja dengan data dari server atau melakukan operasi yang memerlukan waktu lama.
Dengan pemahaman dan penerapan yang tepat, kamu bisa mengatasi berbagai tantangan dalam pengembangan aplikasi web modern.
Asynchronous JavaScript mungkin terlihat rumit pada awalnya, tapi dengan latihan dan pengalaman, kamu akan jadi lebih mahir dalam mengelola operasi asynchronous dengan efisien.
Semoga artikel ini membantu kamu memahami asynchronous JavaScript dengan lebih baik. Selamat ngoding, dan semoga sukses dengan proyek-proyekmu!
Selain itu temen-temen juga bisa kunjungi ebook saya di notion untuk pembahasan yang lebih lengkap serta panduan dari awal sampir akhir: berikut linknya: ebook javascript