Soal 1Di dalam try, apa yang terjadi pada baris-baris di bawah baris yang melakukan await pada Promise yang menjadi rejected?
async / await — Menunggu Hasil Promise Baris demi Baris
Tunggu hasil Promise dengan async / await, tangkap kegagalan dengan try / catch, bandingkan serial dengan Promise.all, dan lihat akibatnya kalau lupa menulis await.
Dengan then pada Promise, value yang datang hanya bisa kamu terima sebagai argumen sebuah fungsi. Kalau hasil pertama ingin kamu pakai di langkah ketiga, kamu harus membuat fungsi bersarang atau menyalin value-nya ke variabel di luar, dan kodenya jadi susah dibaca.
Artikel ini membahas async / await, yang menunggu hasilnya lalu lanjut ke baris berikutnya, serta cara menunggu beberapa Promise sekaligus.
Menulis Langkah dari Atas ke Bawah — Fungsi async dan await
Misalnya kamu memesan slot kosong di tempat parkir, lalu memasukkan nomor slot yang didapat ke dalam pesan. Kalau ditulis dengan then, semua proses yang memakai nomor slot harus ditaruh di dalam fungsi yang kamu berikan ke then, dan kode yang kamu tulis di bawahnya, di luar fungsi itu, sudah dijalankan sebelum nomornya datang.
Di dalam fungsi async (fungsi yang dideklarasikan dengan async dan selalu mengembalikan Promise saat dipanggil), kamu bisa memakai await. await hanya menghentikan sisa fungsi itu sampai Promise-nya selesai, lalu mengambil value fulfilled-nya. Selama berhenti, pemanggil langsung menerima Promise yang masih pending.
// Mengembalikan Promise yang fulfilled dengan value setelah ms milidetik (sama seperti artikel sebelumnya)
function delay(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
const assignSpot = () => delay(100, "B2-17");
// Tulis await di dalam fungsi yang diberi async
async function showParking() {
console.log("Mencari slot parkir kosong"); // Mencari slot parkir kosong
const spot = await assignSpot(); // Tidak lanjut ke baris berikutnya sampai fulfilled
console.log(`Slot ${spot} sudah dipesan`); // Slot B2-17 sudah dipesan
return `Kamu bisa parkir di slot ${spot}`; // Promise yang dikembalikan menjadi fulfilled dengan pesan ini
}
// Pemanggil juga menunggu Promise yang dikembalikan dengan await
const message = await showParking();
console.log(message); // Kamu bisa parkir di slot B2-17
showParking()berjalan sampaiawaitpertama di dalamnya, lalu mengembalikan Promise yang masih pending- Eksekusi tidak sampai ke
console.log(message)sebelum Promise itu fulfilled
- Menampilkan
Mencari slot parkir kosong await assignSpot()— berhenti sampai fulfilled 100ms kemudian- Lanjut ke baris berikutnya setelah
spotberisi"B2-17" - Pesan yang di-return membuat Promise yang dikembalikan menjadi fulfilled
Fungsi async tanpa return pun tetap mengembalikan Promise, yang fulfilled dengan undefined setelah isi fungsinya berjalan sampai akhir. Walaupun kamu tidak butuh value-nya, kalau pemanggilannya diberi await, baris berikutnya baru jalan setelah proses di dalam fungsi selesai.
await di Luar Fungsi Hanya Bisa di Tempat Tertentu
Lingkungan eksekusi di halaman ini membungkus seluruh kodemu dengan fungsi async, jadi kamu juga bisa menulis await di luar fungsi, seperti const message = await showParking();. Di file JavaScript biasa, await di luar fungsi async bisa menjadi SyntaxError. Kalau menulis kode di tempat lain, taruh await di dalam fungsi async.
Memperlakukan Kegagalan sebagai Exception — await dan try / catch
Saat kamu sudah memesan kursi konser lalu membayar dengan kartu, pembayarannya bisa saja gagal. Cara mengambil value dengan await tidak membentuk chain, jadi kegagalannya tidak bisa ditangkap dengan .catch di akhir seperti di artikel sebelumnya.
Kalau kamu throw di dalam fungsi async, Promise yang dikembalikan fungsi itu menjadi rejected. Saat kamu await Promise yang rejected, Error yang sama dilempar di baris await itu, jadi kamu cukup membungkusnya dengan try / catch.
// delay sama dengan kode di bagian pertama
const holdSeat = () => delay(100, "S-204");
const sendReceipt = (seatId) => delay(100, `Struk untuk ${seatId} sudah dikirim`);
// throw di dalam fungsi async membuat Promise yang dikembalikan menjadi rejected
async function payByCard() {
await delay(100); // Hanya menunggu 100ms tanpa memakai value
throw new Error("Kartu sudah kedaluwarsa");
}
try {
const seatId = await holdSeat();
console.log(`Kursi ${seatId} sudah diamankan`); // Kursi S-204 sudah diamankan
await payByCard(); // Rejected, jadi baris ini melempar exception
const receipt = await sendReceipt(seatId); // Baris dari sini ke bawah tidak dijalankan
console.log(receipt);
} catch (error) {
console.log(`Pembelian gagal: ${error.message}`); // Pembelian gagal: Kartu sudah kedaluwarsa
}
catch hanya menerima exception yang dilempar di dalam kurung kurawal try, jadi tulis baris await yang mungkin gagal di dalam try. Proses bersih-bersih yang harus jalan baik berhasil maupun gagal bisa langsung kamu tulis di finally setelah try / catch.
Memulai Tiga Pemanggilan Sekaligus — Serial vs Promise.all
Misalnya saat halaman produk dibuka, kamu mengambil jumlah stok, jumlah ulasan, dan perkiraan tanggal pengiriman. Kalau kamu menulis satu await per baris, setiap request menunggu request sebelumnya selesai dulu baru dimulai, padahal tidak ada baris yang memakai hasil sebelumnya. Request yang tidak saling berhubungan pun jadi ikut mengantre.
Cara menjalankan tugas berikutnya setelah tugas sebelumnya selesai disebut serial, sedangkan cara memulai beberapa tugas sekaligus lalu menunggunya disebut paralel. Untuk menjalankannya secara paralel, panggil ketiga fungsi lebih dulu untuk mendapatkan Promise-nya, berikan ke Promise.all dari artikel sebelumnya, lalu tunggu dengan await, bukan then.
// delay sama dengan kode di bagian pertama
const loadStock = () => delay(120, 8);
const loadReviews = () => delay(50, 23);
const loadArrival = () => delay(90, "13 September");
// Serial: panggil berikutnya setelah hasil sebelumnya datang (lengkap setelah 120 + 50 + 90 = 260ms)
const stock = await loadStock();
const reviews = await loadReviews();
const arrival = await loadArrival();
console.log([stock, reviews, arrival].join(" / ")); // 8 / 23 / 13 September
// Paralel: panggil ketiganya dulu, lalu tunggu (lengkap setelah yang paling lambat, 120ms)
const promises = [loadStock(), loadReviews(), loadArrival()];
const results = await Promise.all(promises);
console.log(results.join(" / ")); // 8 / 23 / 13 September (hasilnya sama)
Kalau kamu menulis await di dalam isi for...of, element array juga diproses secara serial, satu per satu, dan total waktu tunggunya adalah jumlah waktu semua element. Tabel di bawah merangkum kapan memilih serial dan kapan memilih paralel.
| Situasi | Cara menulis | Alasan |
|---|---|---|
| Ambil stok, jumlah ulasan, tanggal kirim | await Promise.all (paralel) | Tak saling memakai hasil, bisa mulai bersamaan |
| Kirim email konfirmasi setelah dapat nomor pesanan | Satu await per baris (serial) | Pemanggilan kedua butuh hasil pemanggilan pertama |
| Proses element array satu per satu | await di dalam isi for...of (serial) | Kalau paralel, urutan selesai bisa beda dari array |
Memanggil Tanpa await — Variabel Berisi Promise
Misalnya kamu menampilkan saldo poin member dan menawarkan hadiah kalau poinnya 1000 atau lebih. Kalau kamu lupa menulis await saat memanggil fungsi async, tidak ada SyntaxError. Yang berubah hanya teks yang tampil dan hasil perbandingannya, sehingga susah menemukan di mana letak kesalahannya.
Fungsi async tidak mengembalikan value yang kamu return, tetapi Promise yang fulfilled dengan value itu. Kalau hasilnya kamu terima tanpa await, variabelnya berisi Promise, dan kalau disisipkan ke template literal, yang tercetak adalah [object Promise] (bentuk Promise saat diubah menjadi string).
// delay sama dengan kode di bagian pertama
async function loadPoints() {
await delay(100);
return 1200; // Promise yang dikembalikan menjadi fulfilled dengan 1200
}
// Kalau lupa menulis await, points berisi Promise
const points = loadPoints();
console.log(`Saldo poin: ${points}`); // Saldo poin: [object Promise]
console.log(points >= 1000); // false (Promise menjadi NaN saat diubah ke angka, jadi perbandingannya gagal)
// Dengan await, variabelnya berisi value fulfilled
const balance = await loadPoints();
console.log(`Saldo poin: ${balance}`); // Saldo poin: 1200
console.log(balance >= 1000); // true
Kalau output-nya menampilkan [object Promise], atau kondisi yang seharusnya terpenuhi malah bernilai false, cek apakah pemanggilan fungsi async-nya sudah memakai await. Kalau ditaruh sebagai kondisi if, Promise adalah object sehingga bernilai truthy, dan kondisinya selalu terpenuhi apa pun value yang dikembalikan fungsinya.
Kegagalan Tanpa await Tidak Sampai ke catch
Di dalam try, kalau kamu memanggil fungsi async yang mengembalikan Promise rejected tanpa await, baris itu tidak melempar exception dan catch tidak pernah dijalankan. Kegagalannya tidak ditangani di mana pun, dan lingkungan eksekusi di halaman ini tidak menampilkan apa-apa. Supaya bisa diterima di catch, tambahkan await pada pemanggilannya.
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Kalau kamu melakukan await pada tiga pemanggilan yang masing-masing butuh 70ms, 30ms, dan 50ms, satu per baris, kira-kira berapa lama sampai ketiganya lengkap?
Soal 3Kalau kamu memanggil fungsi async yang diakhiri return 1200; tanpa await lalu menyimpan hasilnya ke variabel, apa isi variabel itu?