Fungsi callback adalah fungsi yang dioper kepada fungsi lain untuk dipanggil kembali, baik segera setelah pekerjaan tertentu selesai maupun setelah jeda waktu. Nama callback berasal dari makna “dipanggil kembali”, fungsi penerima tidak menjalankan seluruh pekerjaannya sendirian, melainkan memanggil kembali fungsi yang dioperkan kepadanya pada saat yang tepat. Topik ini membangun pola itu dari nol dengan fungsi buatan sendiri, membuktikan urutan eksekusinya pada timer, lalu mengenali pola yang sama pada metode array bawaan. Callback tidak memerlukan sintaks khusus, satu-satunya bekalnya adalah gagasan bahwa fungsi adalah nilai.
Callback sinkron#
Callback paling mudah dipahami dalam bentuk sinkronnya, yaitu dipanggil kembali seketika selama fungsi penerima masih berjalan. Dua fungsi berikut dibangun dari nol. Fungsi keSemua mengiterasi array dan memanggil aksi untuk setiap elemennya tanpa mengumpulkan apa pun, sedangkan ubahSemua memanggil aksi lalu menampung hasilnya. Keduanya menerima callback sebagai argumen kedua dan sama sekali tidak tahu isi callback itu.
Berkas berikut memperagakan keduanya dengan dua callback yang berbeda.
// Callback sinkron, fungsi dioper lalu dipanggil kembali oleh fungsi penerima.
function keSemua(daftar, aksi) {
for (const nilai of daftar) {
aksi(nilai);
}
}
function ubahSemua(daftar, aksi) {
const hasil = [];
for (const nilai of daftar) {
hasil.push(aksi(nilai));
}
return hasil;
}
function tampilkan(nilai) {
console.log("angka:", nilai);
}
function kuadrat(nilai) {
return nilai * nilai;
}
const angka = [3, 5, 8];
keSemua(angka, tampilkan);
console.log(ubahSemua(angka, kuadrat));angka: 3
angka: 5
angka: 8
[ 9, 25, 64 ]Keluarannya memperlihatkan tampilkan dipanggil kembali tiga kali, sekali per elemen, sedangkan kuadrat dipanggil oleh ubahSemua dan hasilnya terkumpul menjadi array 9, 25, 64. Pola keSemua adalah gagasan yang sama dengan metode bawaan forEach, dan pola ubahSemua adalah gagasan di balik map, hanya ditulis manual dengan perulangan for. Diagram berikut merangkum alur pengoperan dan pemanggilan kembali pada satu pemanggilan fungsi.
Callback asinkron dengan setTimeout#
Tidak semua pekerjaan selesai seketika, dan callback juga menjadi cara bahasa ini menunggu. Fungsi setTimeout menerima dua argumen, yaitu callback dan jeda dalam milidetik, lalu memasang jadwal pemanggilannya. Fungsi ini tidak menunggu, baris-baris setelahnya langsung dijalankan, dan callback baru dipanggil kembali setelah jeda terlewati. Perilaku ini disebut asinkron, urutan penulisan tidak sama dengan urutan jalannya.
Berkas berikut memasang callback dengan jeda 200 milidetik di antara dua pencetakan.
// Callback asinkron, setTimeout memanggil kembali fungsi setelah jeda.
console.log("mulai");
setTimeout(() => {
console.log("callback timer tuntas");
}, 200);
console.log("selesai memasang timer");mulai
selesai memasang timer
callback timer tuntasBaris “mulai” dan “selesai memasang timer” tercetak lebih dulu karena setTimeout segera kembali setelah memasang jadwalnya, sedangkan callbacknya tercetak sekitar 200 milidetik kemudian. Program Node tetap hidup sampai timer tuntas lalu berakhir dengan sendirinya, sehingga keluaran tiga baris itu selalu terurut sama. Detail antrean eksekusinya dibahas pada topik Event loop di modul JavaScript asinkron.
Untuk merasakan perbedaan urutan itu di browser, coba demo berikut. Klik tombol Callback sinkron dan tombol Callback timer, lalu bandingkan urutan baris pada log.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Demo callback</title>
<style>
body { font-family: "Segoe UI", Arial, sans-serif; margin: 14px; font-size: 14px; color: #1f2937; }
button { padding: 6px 12px; margin-right: 8px; margin-bottom: 10px; cursor: pointer; }
ol { font-size: 13px; line-height: 1.7; }
</style>
</head>
<body>
<p>Klik salah satu tombol dan perhatikan urutan baris pada log.</p>
<button id="tombol-sinkron" type="button">Callback sinkron</button>
<button id="tombol-timer" type="button">Callback timer</button>
<ol id="log"></ol>
<script src="index.js"></script>
<script>
// Demo callback: callback tombol berjalan segera, callback timer menunggu jeda.
const log = document.getElementById("log");
function catat(pesan) {
const baris = document.createElement("li");
baris.textContent = pesan;
log.appendChild(baris);
}
document.getElementById("tombol-sinkron").addEventListener("click", () => {
catat("klik tercatat, callback tombol berjalan");
catat("aksi sinkron tuntas tanpa jeda");
});
document.getElementById("tombol-timer").addEventListener("click", () => {
catat("klik tercatat, timer dipasang");
setTimeout(() => {
catat("callback timer berjalan setelah 300 ms");
}, 300);
});
</script>
<script>
// Demo callback: callback tombol berjalan segera, callback timer menunggu jeda.
const log = document.getElementById("log");
function catat(pesan) {
const baris = document.createElement("li");
baris.textContent = pesan;
log.appendChild(baris);
}
document.getElementById("tombol-sinkron").addEventListener("click", () => {
catat("klik tercatat, callback tombol berjalan");
catat("aksi sinkron tuntas tanpa jeda");
});
document.getElementById("tombol-timer").addEventListener("click", () => {
catat("klik tercatat, timer dipasang");
setTimeout(() => {
catat("callback timer berjalan setelah 300 ms");
}, 300);
});
</script></body>
</html>Callback pada API bawaan#
Pola callback muncul di banyak API bawaan bahasa ini, dan yang paling sering ditemui adalah metode array seperti map dan forEach. Keduanya menerima callback dan menerapkannya ke setiap elemen, persis seperti ubahSemua dan keSemua yang baru dibangun. Pembahasan penuhnya menunggu modul Metode array dan gaya fungsional. Callback juga menjadi pintu masuk ke tombol dan permintaan jaringan, tetapi fondasi penulisannya sudah lengkap di sini.
const durasiMenit = [1.5, 2.5, 3];
const durasiDetik = durasiMenit.map(menit => menit * 60);
console.log(durasiDetik);
durasiMenit.forEach(menit => {
console.log(menit, "menit");
});[ 90, 150, 180 ]
1.5 menit
2.5 menit
3 menitCallback di atas ditulis sebaris dengan arrow function karena isinya satu transformasi pendek. Bila isinya bertambah panjang, mendeklarasikan fungsi bernama terlebih dahulu lalu mengoper namanya membuat pemanggilannya tetap terbaca.
Ketika callback bertingkat#
Pekerjaan berurutan yang setiap langkahnya harus menunggu ditulis dengan callback bersarang. Dalam bentuk itu, langkah berikutnya berada di dalam callback langkah sebelumnya. Bentuk paling sederhananya memakai timer sebagai pengganti pekerjaan yang menunggu.
// Tiga langkah berurutan lewat setTimeout bertingkat.
console.log("klik tercatat");
setTimeout(() => {
console.log("langkah 1: validasi data");
setTimeout(() => {
console.log("langkah 2: simpan data");
setTimeout(() => {
console.log("langkah 3: muat ulang daftar");
}, 40);
}, 40);
}, 40);klik tercatat
langkah 1: validasi data
langkah 2: simpan data
langkah 3: muat ulang daftarUrutan langkahnya terjaga karena langkah 2 hanya mulai setelah callback langkah 1 berjalan, dan begitu seterusnya. Dengan tiga langkah, sarangnya masih terbaca, tetapi jumlahnya bertambah bersama jumlah langkah. Pola yang menyempit ke kanan ini lazim disebut callback hell, kode bertingkat yang sulit diikuti dan menyulitkan penanganan error.
Ringkasan#
- Callback adalah fungsi yang dioper kepada fungsi lain untuk dipanggil kembali pada saat yang ditentukan oleh fungsi penerimanya.
- Callback sinkron dipanggil seketika selama fungsi penerima berjalan, seperti
aksipadakeSemuadanubahSemuamaupun padaforEachdanmapbawaan. - Callback asinkron dipanggil setelah jeda, seperti callback
setTimeout, sehingga urutan penulisan tidak sama dengan urutan eksekusi. - Callback berurutan yang bersarang dalam disebut callback hell dan menjadi alasan utama hadirnya promise.
Referensi#
- Callbacks — The Modern JavaScript Tutorial — penjelasan pola callback sinkron dan asinkron beserta contohnya.
- Callback function — MDN Web Docs — definisi callback pada glosarium resmi MDN.
- Timers — Node.js
— dokumentasi
setTimeoutdan perilaku timer pada Node.js.