[Medium] π Async/Await
π‘ Disarankan: baca Promise terlebih dahulu untuk memahami konsep dasarnya.
Apa itu async/await?β
async/await adalah syntax sugar yang diperkenalkan di ES2017 (ES8), dibangun di atas Promise.
Fitur ini membuat kode asinkron terlihat lebih seperti kode sinkron, sehingga meningkatkan keterbacaan dan kemudahan pemeliharaan.
Konsep inti:
- Fungsi
asyncselalu mengembalikan sebuah Promise. awaithanya bisa digunakan di dalam fungsiasync.awaitmenghentikan sementara eksekusi fungsi sampai Promise selesai.
Sintaks Dasar (Basic Syntax)β
Fungsi asyncβ
Kata kunci async membuat sebuah fungsi otomatis mengembalikan Promise:
// Gaya Promise tradisional
function fetchData() {
return Promise.resolve('data');
}
// Gaya async (setara)
async function fetchData() {
return 'data'; // otomatis dibungkus menjadi Promise.resolve('data')
}
// pola pemanggilan yang sama
fetchData().then((data) => console.log(data)); // 'data'
Kata kunci awaitβ
await menunggu Promise dan mengembalikan nilai yang di-resolve:
async function getData() {
const result = await Promise.resolve('done');
console.log(result); // 'done'
}
Promise vs async/awaitβ
Contoh 1: permintaan API sederhanaβ
Gaya Promise:
function getUserData(userId) {
return fetch(`/api/users/${userId}`)
.then((response) => response.json())
.then((user) => {
console.log(user);
return user;
})
.catch((error) => {
console.error('Error:', error);
throw error;
});
}
Gaya async/await:
async function getUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
console.log(user);
return user;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
Contoh 2: merangkai beberapa operasi asinkronβ
Gaya Promise:
function processUserData(userId) {
return fetchUser(userId)
.then((user) => {
return fetchPosts(user.id);
})
.then((posts) => {
return fetchComments(posts[0].id);
})
.then((comments) => {
console.log(comments);
return comments;
})
.catch((error) => {
console.error('Error:', error);
});
}
Gaya async/await:
async function processUserData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
console.log(comments);
return comments;
} catch (error) {
console.error('Error:', error);
}
}
Penanganan Error (Error Handling)β
try/catch vs .catch()β
Gunakan try/catch dengan async/await:
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Permintaan gagal:', error);
// Anda bisa menangani berbagai jenis error di sini
if (error.name === 'NetworkError') {
// tangani error jaringan
}
throw error; // lempar ulang atau kembalikan nilai fallback
}
}
Penggunaan campuran (tidak disarankan, tapi bisa jalan):
async function fetchData() {
const response = await fetch('/api/data').catch((error) => {
console.error('Permintaan gagal:', error);
return null;
});
if (!response) return null;
const data = await response.json();
return data;
}
try/catch Bersarang (Nested)β
Gunakan try/catch berlapis ketika langkah-langkah berbeda memerlukan perilaku fallback yang berbeda:
async function complexOperation() {
let user;
try {
user = await fetchUser();
} catch (error) {
console.error('Gagal mengambil data user:', error);
return null;
}
try {
const posts = await fetchPosts(user.id);
return posts;
} catch (error) {
console.error('Gagal mengambil postingan:', error);
return []; // fallback array kosong
}
}
Contoh Praktis (Practical Examples)β
Contoh: alur kerja penilaianβ
Alur: menilai tugas -> cek hadiah -> berikan hadiah -> pemberhentian atau hukuman
// menilai tugas
function correctTest(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const score = Math.round(Math.random() * 100);
if (score >= 60) {
resolve({
name,
score,
});
} else {
reject('Anda telah mencapai ambang batas pemberhentian');
}
}, 2000);
});
}
// cek hadiah
function checkReward(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (data.score >= 90) {
resolve(`${data.name} mendapat tiket bioskop`);
} else if (data.score >= 60 && data.score < 90) {
resolve(`${data.name} mendapat penghargaan prestasi`);
} else {
reject('Tidak ada hadiah');
}
}, 2000);
});
}
Gaya Promise:
correctTest('John Doe')
.then((data) => checkReward(data))
.then((reward) => console.log(reward))
.catch((error) => console.log(error));
Ditulis ulang dengan async/await:
async function processStudent(name) {
try {
const data = await correctTest(name);
const reward = await checkReward(data);
console.log(reward);
return reward;
} catch (error) {
console.log(error);
return null;
}
}
processStudent('John Doe');
Contoh: permintaan bersamaan (concurrent requests)β
Ketika permintaan bersifat independen, jalankan secara bersamaan.
β Salah: eksekusi berurutan (lebih lambat):
async function fetchAllData() {
const users = await fetchUsers(); // tunggu 1 detik
const posts = await fetchPosts(); // 1 detik lagi
const comments = await fetchComments(); // 1 detik lagi
// total 3 detik
return { users, posts, comments };
}
β Benar: eksekusi bersamaan (lebih cepat):
async function fetchAllData() {
// mulai tiga permintaan secara bersamaan
const [users, posts, comments] = await Promise.all([
fetchUsers(),
fetchPosts(),
fetchComments(),
]);
// hanya memakan waktu selama permintaan paling lambat
return { users, posts, comments };
}
Gunakan Promise.allSettled() untuk kegagalan parsial:
async function fetchAllData() {
const results = await Promise.allSettled([
fetchUsers(),
fetchPosts(),
fetchComments(),
]);
const users = results[0].status === 'fulfilled' ? results[0].value : [];
const posts = results[1].status === 'fulfilled' ? results[1].value : [];
const comments = results[2].status === 'fulfilled' ? results[2].value : [];
return { users, posts, comments };
}
Kesalahan Umum (Common Pitfalls)β
1. Menggunakan await di dalam loop (berurutan tanpa sengaja)β
β Salah: menunggu setiap iterasi, performa buruk:
async function processUsers(userIds) {
const results = [];
for (const id of userIds) {
const user = await fetchUser(id); // berurutan, lambat
results.push(user);
}
return results;
}
// 10 user * 1 detik setiap = 10 detik
β
Benar: Promise.all() untuk konkurensi:
async function processUsers(userIds) {
const promises = userIds.map((id) => fetchUser(id));
const results = await Promise.all(promises);
return results;
}
// permintaan bersamaan, sekitar 1 detik total
Kompromi: batasi konkurensi:
async function processUsersWithLimit(userIds, limit = 3) {
const results = [];
for (let i = 0; i < userIds.length; i += limit) {
const batch = userIds.slice(i, i + limit);
const batchResults = await Promise.all(batch.map((id) => fetchUser(id)));
results.push(...batchResults);
}
return results;
}
// proses 3 sekaligus untuk menghindari terlalu banyak permintaan simultan
2. Lupa menggunakan awaitβ
Tanpa await, Anda mendapatkan Promise, bukan nilai yang di-resolve.
// β salah
async function getUser() {
const user = fetchUser(1); // lupa await, user adalah Promise
console.log(user.name); // undefined (Promise tidak punya properti name)
}
// β
benar
async function getUser() {
const user = await fetchUser(1);
console.log(user.name); // nama yang benar
}
3. Menggunakan await tanpa asyncβ
await hanya bisa digunakan di dalam fungsi async.
// β salah: syntax error
function getData() {
const data = await fetchData(); // SyntaxError
return data;
}
// β
benar
async function getData() {
const data = await fetchData();
return data;
}
Top-level await:
Di lingkungan modul ES2022, Anda bisa menggunakan await di level teratas modul:
// modul ES2022
const data = await fetchData();
console.log(data);
4. Tidak menangani errorβ
Tanpa try/catch, error bisa menjadi unhandled rejection.
// β bisa menyebabkan error yang tidak tertangani
async function fetchData() {
const response = await fetch('/api/data'); // throw jika permintaan gagal
return response.json();
}
// β
tambahkan penanganan error
async function fetchData() {
try {
const response = await fetch('/api/data');
return response.json();
} catch (error) {
console.error('Error:', error);
return null; // atau nilai fallback
}
}
5. async selalu mengembalikan Promiseβ
Bahkan tanpa await, fungsi async tetap mengembalikan Promise.
async function getValue() {
return 42; // sebenarnya Promise.resolve(42)
}
// gunakan .then() atau await untuk mendapatkan nilai
getValue().then((value) => console.log(value)); // 42
// atau
async function printValue() {
const value = await getValue();
console.log(value); // 42
}
Penggunaan Lanjutan (Advanced Usage)β
Penanganan timeoutβ
Implementasi timeout dengan Promise.race():
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Permintaan timeout')), ms);
});
}
async function fetchWithTimeout(url, ms = 5000) {
try {
const response = await Promise.race([fetch(url), timeout(ms)]);
return await response.json();
} catch (error) {
console.error('Permintaan gagal:', error.message);
throw error;
}
}
// penggunaan
fetchWithTimeout('/api/data', 3000); // timeout 3 detik
Mekanisme retryβ
Retry otomatis saat gagal:
async function fetchWithRetry(url, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
console.log(`Percobaan ${i + 1} gagal, mencoba ulang dalam ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
// penggunaan
fetchWithRetry('/api/data', 3, 2000); // hingga 3 percobaan, interval 2 detik
Pemrosesan berurutan dengan penyimpanan stateβ
Terkadang eksekusi berurutan diperlukan, sambil menyimpan semua hasil antara:
async function processInOrder(items) {
const results = [];
for (const item of items) {
const result = await processItem(item);
results.push(result);
// tentukan langkah berikutnya berdasarkan hasil sebelumnya
if (result.shouldStop) {
break;
}
}
return results;
}
async/await dalam Event Loopβ
async/await tetap berbasis Promise, sehingga mengikuti aturan Event Loop yang sama:
console.log('1');
async function test() {
console.log('2');
await Promise.resolve();
console.log('3');
}
test();
console.log('4');
// urutan output: 1, 2, 4, 3
Penjelasan:
console.log('1')- sinkrontest()dipanggil,console.log('2')- sinkronawait Promise.resolve()- menjadwalkan kode sisanya sebagai micro taskconsole.log('4')- sinkron- micro task berjalan,
console.log('3')
Poin Penting untuk Wawancara (Interview Key Points)β
async/awaitadalah syntax sugar dari Promise: sintaks lebih bersih, model dasar yang sama.- Gunakan
try/catchuntuk penanganan error: lebih disukai daripada chained.catch()dalam gaya async/await. - Konkurensi vs urutan itu penting: hindari
awaitsecara buta di dalam loop. asyncselalu mengembalikan Promise: bahkan tanpa return Promise secara eksplisit.awaitmemerlukan konteks async: kecuali top-level await di modul ES2022.- Pahami perilaku Event Loop: kode setelah
awaitberjalan sebagai micro task.
Topik Terkait (Related Topics)β
- Promise - fondasi async/await
- Event Loop - model urutan eksekusi