Stok bahan habis di tengah layanan — kegagalan seperti ini bukan soal jika, melainkan kapan. Pada kode sinkron, try-catch dari section Error Handling sudah cukup. Pada kode asinkron, kegagalan terjadi di waktu yang berbeda dari tempat kode dimulai: rejection bisa tiba setelah blok try yang memulainya sejak lama selesai. Karena itu penanganan galat asinkron punya bentuk dan jebakan tersendiri.
Chapter ini merapikan dua gaya penanganan yang setara — .catch() dan try-await-catch — lalu membedah dua jebakan besar: rejection yang dibiarkan tanpa penanganan, dan Promise.all yang berhenti begitu satu anggotanya gagal.
try-catch Biasa Tidak Melihat Rejection #
Kita mulai dari bukti mengapa pola lama tidak bekerja. Cuplikan berikut memulai promise yang pasti gagal dari dalam blok try, tetapi blok catch-nya tidak pernah berjalan:
function pesananLambat(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("gagal saat callback timer")), ms);
});
}
try {
const hasil = pesananLambat(10);
console.log("try selesai tanpa tahu apa-apa; promise masih berjalan");
hasil.catch((error) => console.log("hanya .catch yang melihat:", error.message));
} catch (error) {
console.log("catch try:", error.message); // tidak pernah tercetak
}
console.log("program lanjut seperti biasa");Output yang Dihasilkan:
try selesai tanpa tahu apa-apa; promise masih berjalan
program lanjut seperti biasa
hanya .catch yang melihat: gagal saat callback timerKetika pesananLambat(10) dipanggil, function itu selesai dalam mikrodetik: promise-nya dibuat, lalu eksekusi keluar dari blok try tanpa pernah menemui kegagalan. Kegagalan baru terjadi 10 milidetik kemudian di callback timer — jauh setelah blok try selesai. Satu-satunya pihak yang masih menyertai kegagalan itu adalah handler yang tertempel pada promise-nya sendiri. Inilah alasan galat asinkron harus ditangani lewat jalur promise, bukan lewat blok yang mengelilingi pemanggilannya.
Dua Gaya yang Setara #
Berkas berikut menangkap kegagalan yang sama dengan dua gaya: .catch() pada rantai promise, dan try-catch yang mengapit await:
// penanganan-galat-async.js
// Dua gaya menangkap galat asinkron: .catch() dan try-await-catch.
function seduh(nama, ms, gagal = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (gagal) reject(new Error(`Stok ${nama} habis`));
else resolve(`${nama} siap`);
}, ms);
});
}
// --- 1. gaya then/catch ---
seduh("kopi susu", 10)
.then((hasil) => console.log("[then]", hasil))
.catch((error) => console.log("[then] tertangkap:", error.message));
seduh("matcha", 10, true)
.then((hasil) => console.log("[then]", hasil))
.catch((error) => console.log("[then] tertangkap:", error.message));
// --- 2. gaya async/await + try-catch ---
async function layani(nama, gagal) {
try {
const hasil = await seduh(nama, 10, gagal);
console.log("[await]", hasil);
} catch (error) {
console.log("[await] tertangkap:", error.message);
}
}
layani("teh tarik", false);
layani("cokelat", true);Output yang Dihasilkan:
[then] kopi susu siap
[then] tertangkap: Stok matcha habis
[await] teh tarik siap
[await] tertangkap: Stok cokelat habisMari kita telaah baris-baris pentingnya:
- Baris 4–11 (
seduh): satu pembuat promise dengan dua jalur — parametergagalmenentukan promise berakhir fulfilled atau rejected. - Baris 14–20 (gaya then/catch):
thenuntuk jalur sukses dancatchuntuk jalur gagal menempel pada promise yang sama; keluaran membuktikan keduanya bekerja pada kedua pesanan. - Baris 23–30 (
layani): gaya try-await-catch.awaityang menemui rejection berperilaku sepertithrow, sehingga blokcatchpada baris 27–29 menangkapnya. - Baris 32–33 (dua pemanggilan): keluaran
[await]membuktikan objekErroryang ditangkap persis sama dengan gayathen— pesannya identik strukturnya.
Kedua gaya setara; pilih berdasarkan gaya kode di sekitarnya. .catch() alami di rantai promise, sedangkan try-await-catch alami di function async. Yang penting satu alur memakai satu gaya secara konsisten agar tidak ada jalur kegagalan yang terlewat.
Ketika Rejection Dibiarkan Tanpa Penanganan #
Apa yang terjadi bila tidak ada penanganan sama sekali? Node.js memperlakukan rejection yang tidak tertangani sebagai galat fatal dan menghentikan proses. Bundel chapter ini menyertakan berkas yang sengaja demikian:
// await-tanpa-catch.js
// Demo: rejection tanpa penanganan menghentikan proses Node.js.
// Berkas ini MEMANG gagal dijalankan (keluar kode 1) — lihat transkrip di chapter.
async function prosesStok() {
const hasil = await Promise.reject(new Error("Stok habis"));
return hasil;
}
prosesStok();
console.log("baris terakhir program");Jalankan berkas itu di terminal:
node await-tanpa-catch.js
echo "kode keluar: $?"Keluaran terminal (stdout terlebih dahulu, lalu stderr; baris stack internal Node dipangkas):
baris terakhir program
kode keluar: 1
stderr:
await-tanpa-catch.js:6
const hasil = await Promise.reject(new Error("Stok habis"));
^
Error: Stok habis
at prosesStok (await-tanpa-catch.js:6:38)
...
Node.js v24.21.0Dua hal patut dicatat. Pertama, baris terakhir program tetap tercetak karena log itu sinkron — kegagalan baru dieksekusi runtime sesudahnya. Kedua, proses berakhir dengan kode keluar 1, bukan 0, sehingga skrip yang mengandalkan program ini akan melihatnya sebagai kegagalan.
Satu Gagal di Promise.all, Semua Gagal #
Ketika Promise.all dipakai bersama await, perilaku fail-fast dari chapter sebelumnya berubah bentuk menjadi exception di tengah function:
function muatLaporan(nama, ms, gagal = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (gagal) reject(new Error(`${nama} tidak bisa dimuat`));
else resolve(`${nama} dimuat`);
}, ms);
});
}
async function muatSemua() {
try {
const hasil = await Promise.all([
muatLaporan("januari.csv", 10),
muatLaporan("februari.csv", 10, true),
muatLaporan("maret.csv", 20),
]);
console.log(hasil);
} catch (error) {
console.log("gagal cepat:", error.message);
}
}
muatSemua();Output yang Dihasilkan:
gagal cepat: februari.csv tidak bisa dimuatRejection februari.csv (10 ms) langsung menjadi exception, sehingga hasil maret.csv tidak pernah terbaca. Perlu dicatat pula: promise maret.csv tetap berjalan sampai selesai di latar — fail-fast berhenti menunggu, tetapi tidak menghentikan pekerjaan lain.
allSettled: Alternatif yang Tidak Pernah Melempar #
Ketika kita ingin memproses hasil yang berhasil sekaligus melaporkan yang gagal, Promise.allSettled adalah alat yang tepat karena ia tidak pernah rejected:
function muatLaporan(nama, ms, gagal = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (gagal) reject(new Error(`${nama} tidak bisa dimuat`));
else resolve(`${nama} dimuat`);
}, ms);
});
}
async function muatSemuaDenganLaporan() {
const hasil = await Promise.allSettled([
muatLaporan("januari.csv", 10),
muatLaporan("februari.csv", 10, true),
muatLaporan("maret.csv", 20),
]);
for (const entri of hasil) {
if (entri.status === "fulfilled") {
console.log("berhasil :", entri.value);
} else {
console.log("gagal :", entri.reason.message);
}
}
}
muatSemuaDenganLaporan();Output yang Dihasilkan:
berhasil : januari.csv dimuat
gagal : februari.csv tidak bisa dimuat
berhasil : maret.csv dimuatLoop for-of memisahkan dua kelompok hasil berdasarkan status — pola yang sudah kita kenal dari section Collections. Pesan galat tetap informatif karena reason adalah objek Error utuh, bukan teks terpotong.
Dengan penanganan galat yang rapi, kita kembali ke alat dasar yang paling sering menyertai kode asinkron: timer. Chapter berikutnya membedah setTimeout, setInterval, dan pembatalannya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
await-tanpa-catch.js
Unduh berkas// await-tanpa-catch.js
// Demo: rejection tanpa penanganan menghentikan proses Node.js.
// Berkas ini MEMANG gagal dijalankan (keluar kode 1) — lihat transkrip di chapter.
async function prosesStok() {
const hasil = await Promise.reject(new Error("Stok habis"));
return hasil;
}
prosesStok();
console.log("baris terakhir program");
penanganan-galat-async.js
Unduh berkas// penanganan-galat-async.js
// Dua gaya menangkap galat asinkron: .catch() dan try-await-catch.
function seduh(nama, ms, gagal = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (gagal) reject(new Error(`Stok ${nama} habis`));
else resolve(`${nama} siap`);
}, ms);
});
}
// --- 1. gaya then/catch ---
seduh("kopi susu", 10)
.then((hasil) => console.log("[then]", hasil))
.catch((error) => console.log("[then] tertangkap:", error.message));
seduh("matcha", 10, true)
.then((hasil) => console.log("[then]", hasil))
.catch((error) => console.log("[then] tertangkap:", error.message));
// --- 2. gaya async/await + try-catch ---
async function layani(nama, gagal) {
try {
const hasil = await seduh(nama, 10, gagal);
console.log("[await]", hasil);
} catch (error) {
console.log("[await] tertangkap:", error.message);
}
}
layani("teh tarik", false);
layani("cokelat", true);