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
Array yang Sama, Bentuk Return Value Berbeda
invoices4 tagihan.filtermengecek paid.findmengecek !paid.reducemenjumlah amountArray (2 item)B-301 dan B-303Satu elementtagihan B-302Satu angka12100Lanjut.map atau .joinLanjut.id atau .amountLangsung keconsole.log
Hasilnya bisa berupa array, element, atau angka, dan itu menentukan apa yang bisa ditulis setelahnya. Kalau bentuk hasil yang kamu mau sudah jelas, pilihan method-nya ikut menyempit.

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.

MethodYang dikembalikanPerlu diperhatikan
mapArray baru (jumlahnya sama)Array aslinya tidak berubah
filterArray baru (hanya yang cocok)Array kosong kalau tak ada yang cocok
find / findIndexElement / indexundefined / -1 kalau tidak ditemukan
some / everyBooleanfalse / true pada array kosong
reduceValue return putaran terakhirArray kosong butuh initial value
sort / toSortedArray yang sudah diurutkansort 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')
Chain Hanya Bisa Disambung Selama Hasilnya Array
invoicesarray 4 item.filter(...)array 2 item.map(...)[1200, 2500].reduce(...)3700invoicesarray 4 item.forEach(...)undefined.map(...)pada undefinedBerhenti denganTypeError
Di baris atas, setiap langkah mengembalikan array sampai reduce mengembalikan angka. forEach mengembalikan undefined, jadi chain-nya berhenti di langkah berikutnya.

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
Seberapa Dalam Langkah Berikutnya Ditulis
① Gaya callback — berikan fungsi ke later
  • Panggil later("photo-17.jpg", (fileName) => { … })
Di dalam (fileName) => { … }
  • Hasil pertama sampai di fileName
  • Panggil later kedua di sini
Di dalam (thumbnail) => { … }
  • Hasil kedua sampai di thumbnail
  • Output ditulis di fungsi paling dalam
② then — berikan fungsi ke .then
  • Mulai dari delay(100, "photo-17.jpg")
Di dalam fungsi yang diberikan ke .then pertama
  • Menerima fileName lalu mengembalikan delay berikutnya
Di dalam fungsi yang diberikan ke .then kedua
  • Menerima thumbnail lalu menampilkannya
③ await — terima hasilnya di variabel
  • const fileName = await delay(…)
  • const thumbnail = await delay(…)
  • Tulis console.log(…) di baris berikutnya
Hanya pada gaya callback di ①, fungsi berikutnya masuk ke dalam fungsi yang menerima hasil. await di ③ tidak membuat fungsi apa pun; langkah-langkahnya hanya berupa baris dari atas ke bawah.

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.

GayaTempat hasil diterimaSaat langkahnya bertambah
Gaya callbackArgumen fungsi yang dioperTiap langkah makin dalam
Memberikan fungsi ke thenArgumen fungsi di thenTersambung di kedalaman sama
Menunggu dengan awaitVariabel di sisi kiriDitulis di baris berikutnya

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)
Tiga Tempat yang Menerima Kegagalan yang Sama
checkStock throwkalau lebih dari 3① checkStock(5)dipanggil langsung② reserveStock(5).catch(...)③ awaitreserveStock(5)Baris pemanggilanmelempar exceptionPromise yangdikembalikanmenjadi rejectedBaris awaitmelempar exceptionBlok catchsetelah tryArgumen fungsidi .catchBlok catchsetelah try
Di ②, baris pemanggilannya tidak melempar exception, melainkan Promise yang dikembalikan menjadi rejected. Blok catch hanya menerima ① dan ③, karena di situ barisnya sendiri yang melempar exception.

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 ditulisFungsinyaPerlu diperhatikan
try / catchMenerima exception di dalamnyacatch kosong menutupi kegagalan
finallySelalu jalan paling akhirTempat proses bersih-bersih
throw new ErrorMelempar exception saat itu juganew Error saja tidak menghentikan
class extends ErrorJenis untuk dicek instanceofCek jenis buatanmu sebelum Error
cause pada ErrorMelampirkan exception aslinyaBaca dengan error.cause
.catch pada PromiseDipanggil saat rejectedTambahkan di akhir chain then
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Apa yang dikembalikan oleh invoices.filter((invoice) => invoice.id === "B-302")?

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?