Soal 1Apa yang dikembalikan oleh invoices.filter((invoice) => invoice.id === "B-302")?
Ringkasan Sintaks — Tabel Cepat Array Method, Asinkron, dan Exception
Tabel cepat untuk array method berdasarkan return value-nya, perbandingan callback, then, dan await, serta tempat setiap jenis kegagalan ditangkap.
Menjumlahkan daftar tagihan, menunggu upload selesai, menangkap kegagalan lalu menampilkan pesan: masing-masing bisa ditulis dengan beberapa cara. Kalau salah pilih, kamu bisa mendapat undefined padahal mengharapkan array, atau kegagalannya tidak sampai ke catch mana pun.
Artikel ini membahas kapan memakai array method yang mana, cara menunggu proses asinkron, dan cara menangkap exception.
Memilih Berdasarkan Bentuk Hasil yang Kamu Mau — Return Value Array Method
Misalnya dari daftar tagihan kamu ingin mendapatkan tiga hal: berapa tagihan yang sudah dibayar, tagihan pertama yang belum dibayar, dan total nominalnya. Semua array method dipanggil dengan cara yang sama, yaitu dengan memberikan callback, jadi hanya dengan melihat pemanggilannya, kamu tidak bisa tahu apakah hasilnya berupa array atau satu value saja.
Di sini, method dikelompokkan berdasarkan bentuk return value-nya: array baru, salah satu element yang ada di dalam array, atau value yang dihitung dari array, seperti boolean atau total. Kalau bentuknya sudah jelas, kamu juga tahu apakah hasilnya bisa dilanjutkan dengan join atau dibaca property-nya seperti .id.
const invoices = [
{ id: "B-301", amount: 4800, paid: true },
{ id: "B-302", amount: 1200, paid: false },
{ id: "B-303", amount: 3600, paid: true },
{ id: "B-304", amount: 2500, paid: false },
];
// Mengembalikan array: map jumlahnya tetap, filter hanya yang cocok
console.log(invoices.map((invoice) => invoice.id).join(", ")); // B-301, B-302, B-303, B-304
console.log(invoices.filter((invoice) => invoice.paid).length); // 2
// Mengembalikan element itu sendiri: element pertama yang cocok
console.log(invoices.find((invoice) => !invoice.paid).id); // B-302
// Mengembalikan value yang dihitung dari array: some memberi boolean, reduce memberi hasil akumulasi
console.log(invoices.some((invoice) => invoice.amount >= 4000)); // true
console.log(invoices.reduce((total, invoice) => total + invoice.amount, 0)); // 12100
Kalau kamu memakai filter padahal hanya butuh satu item, yang dikembalikan adalah array berisi satu item, jadi kamu harus menambahkan [0] sebelum membaca .id. Tabel di bawah mengelompokkan array method yang menerima callback dan sudah dibahas di kategori ini, berdasarkan bentuk return value-nya.
| Method | Yang dikembalikan | Perlu diperhatikan |
|---|---|---|
| map | Array baru (jumlahnya sama) | Array aslinya tidak berubah |
| filter | Array baru (hanya yang cocok) | Array kosong kalau tak ada yang cocok |
| find / findIndex | Element / index | undefined / -1 kalau tidak ditemukan |
| some / every | Boolean | false / true pada array kosong |
| reduce | Value return putaran terakhir | Array kosong butuh initial value |
| sort / toSorted | Array yang sudah diurutkan | sort juga mengubah array aslinya |
Kapan Chain Bisa Dilanjutkan — Method Chaining dan forEach
Misalnya kamu hanya ingin menjumlahkan tagihan yang belum dibayar. Kalau hasil filter kamu simpan di variabel, lalu map dipanggil pada variabel itu dan disusul reduce, kamu jadi harus memberi nama pada array perantara yang tidak dipakai lagi. Sebaliknya, kalau kamu menulis semuanya dalam satu ekspresi tanpa tahu method mana yang bisa disambung setelah method mana, kamu bisa mendapat TypeError di titik sambungannya.
Dalam chaining (cara memanggil method berikutnya pada return value dari method sebelumnya), setiap pemanggilan seperti .filter(...) dihitung sebagai satu langkah, dan setiap langkah dipanggil pada value yang dikembalikan langkah sebelumnya. Array method hanya bisa dilanjutkan kalau langkah sebelumnya mengembalikan array.
// invoices sama dengan 4 tagihan di bagian sebelumnya
// Selama hasilnya berupa array, kamu bisa terus menambahkan array method
const unpaidTotal = invoices
.filter((invoice) => !invoice.paid) // Array: 2 tagihan, B-302 dan B-304
.map((invoice) => invoice.amount) // Array: [1200, 2500]
.reduce((total, amount) => total + amount, 0); // Angka: 3700
console.log(unpaidTotal); // 3700
// toSorted juga mengembalikan array, jadi bisa dilanjutkan sampai map dan join
const idsByAmount = invoices.toSorted((a, b) => b.amount - a.amount).map((invoice) => invoice.id);
console.log(idsByAmount.join(", ")); // B-301, B-303, B-304, B-302
// forEach mengembalikan undefined, jadi apa pun yang disambung setelahnya akan gagal
invoices.forEach((invoice) => console.log(invoice.id)).map((invoice) => invoice.id);
// Setelah menampilkan B-301 sampai B-304
// TypeError: Cannot read properties of undefined (reading 'map')
Taruh langkah yang tidak mengembalikan array di akhir chain. Setelah find, kamu bisa membaca property seperti .id, tetapi tidak bisa memanggil map, dan angka yang dikembalikan reduce cukup disimpan di variabel lalu dipakai.
Memanggil map Setelah find Menghasilkan Pesan yang Berbeda
Kalau kamu menyambung .map(...) ke element yang dikembalikan find atau ke angka yang dikembalikan reduce, eksekusi berhenti dengan TypeError seperti invoices.find(...).map is not a function. Pesannya berbeda dengan pesan setelah forEach, tetapi di kedua kasus, langkah tepat sebelum .map tidak mengembalikan array.
Membandingkan Cara Menunggu — Callback / then / await
Misalnya kamu meng-upload foto lalu membuat thumbnail-nya. Keduanya butuh waktu sampai hasilnya datang, jadi fungsinya tidak bisa menyerahkan hasil sebagai return value, dan langkah kedua harus menunggu sampai hasil langkah pertama datang.
Di kode di bawah, ① memakai gaya callback (kamu memberikan fungsi kedua ke fungsi yang memulai proses, lalu fungsi kedua itu dipanggil saat prosesnya selesai; tidak ada Promise yang dikembalikan), dan setTimeout juga bekerja dengan cara ini. ② memberikan fungsi ke then pada Promise yang dikembalikan, dan ③ memakai await di dalam fungsi async.
// ① Gaya callback: berikan fungsi yang dipanggil saat selesai, lalu tulis langkah berikutnya di dalamnya
// later memanggil callback dengan value setelah 100ms (tidak mengembalikan Promise)
function later(value, callback) {
setTimeout(() => callback(value), 100);
}
later("photo-17.jpg", (fileName) => {
later(`Thumbnail ${fileName}`, (thumbnail) => {
console.log(`① ${thumbnail} sudah dibuat`); // ① Thumbnail photo-17.jpg sudah dibuat
});
});
// ② then: sambungkan langkah berikutnya dengan .then (delay sama seperti di artikel Promise)
delay(100, "photo-17.jpg")
.then((fileName) => delay(100, `Thumbnail ${fileName}`))
.then((thumbnail) => console.log(`② ${thumbnail} sudah dibuat`)); // ② Thumbnail photo-17.jpg sudah dibuat
// ③ await: tulis langkah berikutnya di baris bawah dan terima hasilnya di variabel (dengan asumsi berjalan di dalam fungsi async, seperti di artikel sebelumnya)
const fileName = await delay(100, "photo-17.jpg");
const thumbnail = await delay(100, `Thumbnail ${fileName}`);
console.log(`③ ${thumbnail} sudah dibuat`); // ③ Thumbnail photo-17.jpg sudah dibuat
- Panggil
later("photo-17.jpg", (fileName) => { … })
- Hasil pertama sampai di
fileName - Panggil
laterkedua di sini
- Hasil kedua sampai di
thumbnail - Output ditulis di fungsi paling dalam
- Mulai dari
delay(100, "photo-17.jpg")
- Menerima
fileNamelalu mengembalikandelayberikutnya
- Menerima
thumbnaillalu menampilkannya
const fileName = await delay(…)const thumbnail = await delay(…)- Tulis
console.log(…)di baris berikutnya
Fungsi di then kedua hanya menerima thumbnail, jadi kalau ingin memakai fileName juga di sana, kamu harus menyalinnya ke variabel di luar. Dengan await, fileName tersimpan di variabel yang bisa dibaca dari baris mana pun di bawahnya. Tabel di bawah membandingkan ketiga gaya itu.
Mengecek ke Mana Kegagalan Sampai — try / catch dan .catch
Misalnya fungsi yang memesan stok akan gagal kalau jumlah pesanan melebihi stok yang ada. Untuk fungsi biasa yang tidak mengembalikan Promise, kegagalannya bisa ditangkap dengan membungkus baris pemanggilannya dengan try / catch. Namun, kegagalan dari fungsi yang mengembalikan Promise bisa sampai ke blok catch atau tidak, tergantung cara kamu menulis pemanggilannya.
Blok catch yang ditulis setelah try menerima exception yang dilempar di baris-baris yang dibungkusnya. .catch milik Promise memanggil fungsi yang kamu berikan saat Promise itu menjadi rejected.
function checkStock(count) {
if (count > 3) {
throw new Error(`Stok tidak cukup: ${count} item`);
}
}
async function reserveStock(count) {
await delay(100); // delay sama seperti di artikel Promise
checkStock(count); // throw di sini membuat Promise yang dikembalikan fungsi ini menjadi rejected
}
// ① Fungsi yang tidak mengembalikan Promise: bungkus baris pemanggilannya dengan try
try { checkStock(5); } catch (error) { console.log(`① ${error.message}`); }
// ② Gaya then: berikan fungsi penerima ke .catch
reserveStock(5).catch((error) => console.log(`② ${error.message}`));
// ③ Gaya await: bungkus baris await dengan try
try { await reserveStock(5); } catch (error) { console.log(`③ ${error.message}`); }
// ① Stok tidak cukup: 5 item
// ② Stok tidak cukup: 5 item (setelah 100ms)
// ③ Stok tidak cukup: 5 item (setelah 100ms)
Baik diterima dengan .catch maupun blok catch, Error yang sampai tetap sama, jadi pengecekan error.message dan instanceof bisa dipakai di keduanya. Tabel di bawah merangkum cara menangani exception yang sudah dibahas di kursus ini.
| Yang ditulis | Fungsinya | Perlu diperhatikan |
|---|---|---|
| try / catch | Menerima exception di dalamnya | catch kosong menutupi kegagalan |
| finally | Selalu jalan paling akhir | Tempat proses bersih-bersih |
| throw new Error | Melempar exception saat itu juga | new Error saja tidak menghentikan |
| class extends Error | Jenis untuk dicek instanceof | Cek jenis buatanmu sebelum Error |
| cause pada Error | Melampirkan exception aslinya | Baca dengan error.cause |
| .catch pada Promise | Dipanggil saat rejected | Tambahkan di akhir chain then |
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Pada invoices.filter(...).map(...).reduce(...), array mana yang diakumulasi oleh reduce?
Soal 3Kalau kamu menyambung tiga langkah asinkron dengan gaya callback, di mana langkah ketiga ditulis?