- Apa itu callback?
- Mengapa butuh callback?
- Yuk buat callback!
- Real-world case study
- Callback hell
- Referensi
Secara sederhananya: Callback merupakan sebuah fungsi yang akan dijalankan setelah sebuah fungsi lain telah selesai dijalankan, sehingga, dinamakan dengan callback
Secara kompleksnya: Dalam javascript, perlu diingat bahwa fungsi adalah sebuah objek atau sering disebut dengan first-class object, yang artinya:
- fungsi bisa dijadikan sebagai parameter
- fungsi bisa disimpan ke dalam variabel
- fungsi bisa memiliki property dan method
Javascript sendiri merupakan event-driven language. Ini artinya dalam Javascript, ketimbang menunggu sebuah respon berjalan, Javascript akan mengeksekusi sesuatu sambil menunggu event lainnya.
Code:
function fungsiPertama() {
console.log("satu");
}
function fungsiKedua() {
console.log("dua");
}
fungsiPertama();
fungsiKedua();
Output:
satu
dua
Penjelasan:
Pada kode di atas, fungsiPertama
akan dijalankan terlebih dahulu sebelum
fungsiKedua
. semua terkesan baik-baik saja.
Tapi apa yang terjadi bila fungsiPertama
memiliki kode yang tidak bisa
dijalankan dengan cepat? Contohnya API request dimana harus mengirim request
dan menunggu response?
Untuk mensimulasikan ini, kita akan mengubah kode kita di atas dengan menambahkan
fungsi bawaan Javascript setTimeout
Code 2:
function fungsiPertama() {
//Simulasi delay sebagai analogi API Request
setTimeout( () => {
console.log("satu");
}, 500);
}
function fungsiKedua() {
console.log("dua");
}
fungsiPertama();
fungsiKedua();
Output 2:
dua
satu
Pertanyaan:
Loh, mengapa output-nya menjadi dua terlebih dahulu baru satu?
Padahal kan fungsiPertama
dipanggil terlebih dahulu sebelum fungsiKedua
?
Penjelasan:
Ini bukan berarti Javascript ngeyel dengan tidak menjalankan fungsiPertama
dahulu baru menjalankan fungsiKedua
, hanya saja Javascript tidak menunggu
respon dari fungsiPertama
sebelum menjalankan fungsiKedua
.
Jadi pada javascript, kita tidak bisa mengharapkan dengan memanggil fungsi secara berurutan dan berharap urutan tersebut akan dijalankan dengan benar.
Solusinya bagaimana? salah satunya adalah dengan menggunakan callback.
Masih dengan contoh yang sama di atas, kita akan memodifikasi kode sehingga
walaupun fungsiPertama
menggunakan setTimeout
sebagai analogi API Request,
namun tetap "ditunggu" oleh fungsiKedua
Code 3:
//fungsiPertama akan menerima sebuah parameter dengan nama cb yang merupakan callback
function fungsiPertama(cb) {
//Simulasi delay sebagai analogi API Request
setTimeout( () => {
console.log("satu");
//panggil parameter cb, as a function.
cb();
}, 500);
}
function fungsiKedua() {
console.log("dua");
}
//masukkan fungsiKedua sebagai parameter fungsiPertama
fungsiPertama(fungsiKedua);
Output 3:
satu
dua
Penjelasan:
Pada kode di atas, fungsiPertama
sekarang menerima sebuah parameter bernama
cb
yang merupakan sebuah callback. Kemudian setelah mencetak tulisan
satu (yang dianalogikan setelah API Request berhasil diperoleh), kita akan
memanggil parameter cb
sebagai fungsi.
Saat memanggil fungsiPertama
, kita memasukkan sebuah parameter, yaitu
fungsiKedua
yang akan dijadikan sebagai callback-nya. sehingga setelah
mencetak tulisan satu, kita akan menggail fungsiKedua
untuk mencetak
tulisan dua
Pada code sebelumnya, disinggung analogi tentang API Request. cara sederhana pada javascript untuk melakukan API Request adalah dengan menggunakan AJAX, namun, umumnya untuk AJAX terdapat masalah seperti berikut:
Code 4:
let data = requestDataViaAjax();
showResult(data);
Problem:
Pada kode di atas, fungsi showResult()
bisa jadi mengembalikan data undefined
dikarenakan fungsi requestDataViaAjax()
merupakan fungsi async, dimana seharusnya
kode di atas menunggu respon requestDataViaAjax()
selesai dilakukan
terlebih dahulu baru memanggil fungsi showResult()
.
Solusi:
Solusinya adalah dengan mengubah fungsi showResult()
menjadi callback bagi
fungsi requestDataViaAjax()
.
Solution 4:
(dapat dilihat di link github ini)
const requestDataViaAjax = (cb) => {
let xhr = new XMLHttpRequest();
let url = "https://jsonplaceholder.typicode.com/users/1";
xhr.open('GET', url);
xhr.onload = () => {
if(xhr.status === 200) {
cb(xhr.responseText);
}
else {
cb(null);
}
}
xhr.send();
}
const showResult = (data) => {
if (data != null) {
data = JSON.parse(data);
console.log(data);
}
}
requestDataViaAjax(showResult);
Dalam NodeJS, operasi file seperti baca dan tulis tulis umum digunakan. pada NodeJS sendiri operasi ini mendukung secara sync dan async. pada pembelajaran ini kita akan mencoba untuk membuat kode sederhana untuk membaca file secara async.
Code 5:
(dapat dilihat di link github ini)
//modul fs dari nodejs
const fs = require('fs');
//fungsi yang digunakan sebagai callback
const callbackBacaFile = (error, data) => {
if(error) {
console.log("Error dalam membaca file: " + error);
}
else {
console.log(data.toString());
}
};
//memanggil fungsi readFile untuk membaca data hello.md
let data = fs.readFile('hello.md', callbackBacaFile);
Output 5:
ini hanyalah tulisan.
Callback hell merupakan kondisi ketika programmer membuat beberapa callback di dalam callback (nested callback) sehingga membuat codingan yang sudah ada sulit untuk dibaca.
Contoh 6:
fs.readFile('hello1.md', (a) => {
fs.readFile('hello2.md', (b) => {
fs.readFile('hello3.md', (c) => {
fs.writeFile('output.md', a+b+c, () => {
console.log("File selesai dituliskan !");
});
});
});
});
Problem 6:
Dapat dilihat dari kode di atas, betapa banyaknya })
di akhir? Oh My God,
kondisi seperti inilah yang disebut dengan Callback Hell.
Kondisi seperti ini membuat:
- Kode lebih sulit dibaca, kode piramid seperti ini akan sulit untuk di-maintain nantinya
- Kode di atas juga belum memasukkan adanya error handling, sehingga jika proses baca atau tulisnya error, sulit untuk dideteksi.
Solusi 6:
Solusi dari permasalahan ini adalah:
- Jangan menggunakan fungsi dalam fungsi seperti contoh kode di atas.
- Biasakan membuat kode lebih modular agar lebih mudah dibaca (pecah fungsi dan beri nama)
- Handle setiap error yang ada di dalam setiap callback.
- Mengggunakan alternatif dari callback yang sudah disediakan dalam Javascript (Promise atau async / await)
- Solusi untuk permasalahan di atas (tanpa error handling) dapat dilihat pada link ini