Section ini sejak awal menyebut function sebagai nilai — dan selama ini nilai yang tersimpan itu kita panggil sendiri. JavaScript membawa ide itu satu langkah lebih jauh: function boleh dikirim kepada function lain sebagai argument, agar dipanggil oleh function tersebut pada saat yang tepat. Function yang dikirim begini dinamakan callback — secara harfiah, “dipanggil kembali” oleh penerimaannya.

Pola ini mendiami hampir seluruh JavaScript modern: method array memanggil callback untuk setiap elemennya, event browser memanggil callback saat pengguna menekan tombol, dan operasi jaringan memanggil callback begitu datanya datang. Chapter ini membangun polanya dari nol dengan helper sederhana buatan kita sendiri, lalu menyentuh wajah asynchronous-nya secara singkat.

Helper untukSetiap: Callback Mini Buatan Sendiri #

Helper yang kita butuhkan melakukan satu hal: untuk setiap elemen daftar, jalankan aksi dengan elemen itu. Perhatikan bahwa untukSetiap tidak tahu apa pun tentang isi pekerjaannya — tidak tahu apakah elemennya akan dicetak, dihitung, atau dicocokkan. Pekerjaan itu ditentukan pemanggil lewat callback yang dikirim; helper hanya menyediakan ritmenya, yaitu satu pemanggilan per elemen beserta posisinya:

// helper-aksi.js
// Callback: function dikirim sebagai argument untuk dipanggil function lain.

// --- 1. helper sederhana: menjalankan aksi untuk setiap elemen ---
function untukSetiap(daftar, aksi) {
  for (let i = 0; i < daftar.length; i++) {
    aksi(daftar[i], i);
  }
}

const buah = ["apel", "mangga", "jeruk"];

untukSetiap(buah, function (item, posisi) {
  console.log(posisi + ": " + item);
});

// --- 2. callback berbeda, daftar yang sama ---
function laporkan(item, posisi) {
  console.log("Cek stok " + item + " (urutan " + posisi + ")");
}

untukSetiap(buah, laporkan);

// --- 3. callback yang membawa aturan sendiri ---
function tandaiSelesai(item, posisi) {
  if (posisi === buah.length - 1) {
    console.log("[TERAKHIR] " + item);
  } else {
    console.log("[belum] " + item);
  }
}

untukSetiap(buah, tandaiSelesai);

// --- 4. callback yang berjalan belakangan (asynchronous, singkat) ---
console.log("-- pemesanan mulai --");
setTimeout(function () {
  console.log("-- pesanan selesai diseduh --");
}, 300);
console.log("-- program lanjut tanpa menunggu --");

Output yang Dihasilkan:

text
0: apel
1: mangga
2: jeruk
Cek stok apel (urutan 0)
Cek stok mangga (urutan 1)
Cek stok jeruk (urutan 2)
[belum] apel
[belum] mangga
[TERAKHIR] jeruk
-- pemesanan mulai --
-- program lanjut tanpa menunggu --
-- pesanan selesai diseduh --

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 5–9 (function untukSetiap(daftar, aksi)): parameter kedua bernama aksi menerima function. Baris 7 memanggilnya tiga kali — sekali per elemen — mengirim elemen dan posisinya. Di sinilah kata callback bermakna: aksi tidak dipanggil saat berkas dimuat, melainkan dipanggil-kembalikan oleh untukSetiap pada setiap putaran.
  2. Baris 13–15 (callback anonim langsung di pemanggilan): function expression ditulis langsung di dalam tanda kurung pemanggilan. Bentuk ini sangat lazim bila callback hanya dipakai sekali.
  3. Baris 18–22 (laporkan dan pemanggilannya): callback juga boleh function bernama yang sudah didefinisikan — perhatikan laporkan ditulis tanpa tanda kurung saat dikirim. Menambahkan laporkan() di sini berarti memanggil dan mengirim hasilnya (undefined), bukan mengirim function-nya — kekeliruan klasik pemula.
  4. Baris 25–33 (tandaiSelesai): callback bebas membawa logika sendiri, termasuk if. Satu helper kini melayani tiga perilaku berbeda tanpa satu baris pun berubah di dalamnya — pemisahan antara cara menelusuri daftar dan apa yang dilakukan pada tiap elemen.
  5. Baris 36–40 (setTimeout dan sekitarnya): setTimeout menerima callback dan menjalankannya setelah 300 milidetik. Baris 40 mencetak lebih dahulu; callback baru menyusul terakhir. Program tidak berhenti menunggu — inilah inti callback asynchronous.

Satu Helper, Banyak Perilaku #

Tiga pemanggilan untukSetiap memakai data yang sama tetapi menghasilkan tiga keluaran berbeda, semata karena callback-nya berbeda. Pola “mekanisme di helper, kebijakan di callback” inilah yang membuat kode JavaScript terasa modular: helper yang sama bisa dipakai ulang untuk kebutuhan yang belum kita bayangkan saat menulisnya. Bila kelak section Arrays memperkenalkan method bawaan forEach, bentuk pemakaiannya akan terasa familier — persis seperti helper kita, dengan jaminan performa dan perilaku yang teruji.

Callback yang Menerima Hasil Perhitungan #

Sejauh ini callback melakukan pekerjaan sendiri. Arah sebaliknya juga sama pentingnya: helper yang menghitung lalu menyerahkan hasilnya kepada callback untuk ditindaklanjuti. Dengan begitu, satu helper bisa menyajikan data yang sama dalam beberapa bentuk tanpa menghitung ulang:

js
function hitungTotal(daftar, pemroses) {
  let total = 0;
  for (let i = 0; i < daftar.length; i++) {
    total = total + daftar[i];
  }
  pemroses(total);
}

hitungTotal([2000, 3500, 1500], function (hasil) {
  console.log("Total belanja: Rp" + hasil);
});

hitungTotal([2000, 3500, 1500], function (hasil) {
  console.log("Rata-rata: Rp" + Math.round(hasil / 3));
});

Output yang Dihasilkan:

text
Total belanja: Rp7000
Rata-rata: Rp2333

hitungTotal menghitung jumlah seluruh elemen sekali, lalu memanggil pemroses(total) — callback dengan satu parameter yang berisi hasil. Callback pertama menampilkannya apa adanya; callback kedua mengolahnya lebih jauh menjadi rata-rata. Pemisahan ini adalah inti banyak API JavaScript: function yang menghitung tidak perlu tahu bagaimana hasilnya akan dipakai, dan pemakai tidak perlu tahu bagaimana hasilnya dihitung. Math.round yang dipakai di sini adalah function bawaan pembulat ke bilangan terdekat — salah satu dari banyak utilitas angka yang tersedia tanpa diimpor.

Callback Asynchronous dalam Sekilas Pandang #

Empat baris terakhir berkas contoh memperlihatkan perbedaan waktu yang penting. Pemanggilan untukSetiap menjalankan callback-nya seketika (synchronous): baris 15 baru selesai setelah seluruh elemen tercetak. setTimeout bersikap sebaliknya: ia mencatat callback, menjadwalkannya, lalu langsung kembali — sehingga -- program lanjut tanpa menunggu -- tercetak lebih dulu, dan callback baru dieksekusi setelah waktunya tiba. Urutan tiga baris terakhir pada keluaran di atas selalu konsisten persis karena itulah.

Pola yang sama menopang interaktivitas browser. Saat kita menuliskan “jalankan function ini ketika pengguna menekan tombol”, function yang dikirim adalah callback — browser yang menyimpannya, dan browser yang memanggilnya, tetapi waktunya ditentukan tindakan pengguna, bukan urutan baris. Detail penuhnya dibahas di section Browser Events; untuk sekarang cukup dipahami bahwa callback yang kita bangun di helper ini adalah bentuk yang persis sama.

Tiga kebiasaan kecil menjaga kode callback tetap sehat. Pertama, saat mengirim function bernama, tulis namanya tanpa tanda kurung — laporkan, bukan laporkan(). Kedua, jaga badan callback tetap pendek; logika yang panjang lebih baik menjadi function bernama sendiri agar bisa diuji terpisah. Ketiga, ingat bahwa callback dipanggil oleh pihak lain: kita tidak mengendalikan kapan ia berjalan, sehingga callback tidak boleh bergantung pada asumsi waktu yang kita ciptakan sendiri.

Callback menutup lingkaran ide “function sebagai nilai”: dikirim, dipanggil pihak lain, bahkan dijadwalkan untuk masa depan. Callback juga membuka pertanyaan baru yang menggantung sejak tadi: dari mana callback anonim di baris 14 tahu bisa membaca variabel? Dan apa yang terjadi pada variabel-variabel di sekitarnya setelah function selesai dipanggil? Dua pertanyaan itu adalah urusan scope — topik chapter berikutnya, yang sekaligus menjadi fondasi konsep paling penting di section ini: closure.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

helper-aksi.js

Unduh berkas
javascript
// helper-aksi.js
// Callback: function dikirim sebagai argument untuk dipanggil function lain.

// --- 1. helper sederhana: menjalankan aksi untuk setiap elemen ---
function untukSetiap(daftar, aksi) {
  for (let i = 0; i < daftar.length; i++) {
    aksi(daftar[i], i);
  }
}

const buah = ["apel", "mangga", "jeruk"];

untukSetiap(buah, function (item, posisi) {
  console.log(posisi + ": " + item);
});

// --- 2. callback berbeda, daftar yang sama ---
function laporkan(item, posisi) {
  console.log("Cek stok " + item + " (urutan " + posisi + ")");
}

untukSetiap(buah, laporkan);

// --- 3. callback yang membawa aturan sendiri ---
function tandaiSelesai(item, posisi) {
  if (posisi === buah.length - 1) {
    console.log("[TERAKHIR] " + item);
  } else {
    console.log("[belum] " + item);
  }
}

untukSetiap(buah, tandaiSelesai);

// --- 4. callback yang berjalan belakangan (asynchronous, singkat) ---
console.log("-- pemesanan mulai --");
setTimeout(function () {
  console.log("-- pesanan selesai diseduh --");
}, 300);
console.log("-- program lanjut tanpa menunggu --");