Apa Perbedaan Antara Callback Dan Promise Dalam JavaScript?

Oleh: Ahmad Mu'azim Abidin

Dalam pengembangan aplikasi modern, kemampuan mengelola operasi asinkron (asynchronous) seperti mengambil data dari API, membaca file, atau mengatur timer adalah hal yang krusial. JavaScript mengelola hal ini menggunakan beberapa mekanisme. Dua mekanisme fundamental yang paling sering diperdebatkan adalah Callback dan Promise.


Callback adalah fondasi awal JavaScript dalam menangani operasi asinkron, namun ia memiliki keterbatasan besar dalam skala kode dan manajemen error karena masalah Callback Hell.


Promise hadir sebagai standar industri modern yang mengubah struktur penulisan kode asinkron dari yang semula "bersarang" (nested) menjadi "berantai" (sequential), memberikan kontrol yang lebih baik, pengelolaan error yang bersih, serta membuka jalan bagi sintaks yang lebih modern lagi, yaitu async/await.


Ringkasan mengenai perbedaan keduanya:

  1. Callback: Pendekatan Tradisional (The Foundation): Secara definisi, Callback adalah fungsi yang dilewatkan sebagai argumen ke dalam fungsi lain, dengan ekspektasi akan dieksekusi setelah suatu aksi atau operasi asinkron selesai. Callback Hell merupakan kelemahan utama ketika sebuah aplikasi berkembang dan membutuhkan banyak operasi asinkron yang saling bergantung (misal: Ambil User => Ambil Posts => Ambil Komentar => Kirim Log), kode akan menjorok ke dalam berbentuk segitiga. Fenomena ini disebut Callback Hell atau Pyramid of Doom, yang membuat kode sangat sulit dibaca, dirawat, dan didebug.
  2. Promise: Solusi Modern (The Evolution): Untuk mengatasi masalah Callback Hell, ES6 (ECMAScript 2015) memperkenalkan Promise. Secara definisi, Promise adalah sebuah objek yang mewakili penyelesaian (atau kegagalan) dari suatu operasi asinkron di masa depan. Promise bertindak sebagai placeholder untuk nilai yang belum diketahui saat fungsi dijalankan. Promise memiliki 3 state (status): Pending: Operasi sedang berjalan, belum selesai, Fulfilled: Operasi sukses dan menghasilkan nilai (resolved), Rejected: Operasi gagal dan menghasilkan error.

Untuk proyek skala menengah hingga besar saat ini, menggunakan Promise (atau kombinasinya dengan async/await) adalah best practice yang wajib diterapkan demi menjaga kualitas dan performa kode.


Kode Callback

// Simulasi fungsi asinkron dengan Callback
function getUser(id, callback) {
    setTimeout(() => {
        console.log("1. Mengambil data user dari database...");
        callback({ id: id, username: "ahmad_muazim" });
    }, 1500);
}

function getPosts(username, callback) {
    setTimeout(() => {
        console.log(`2. Mengambil artikel milik ${username}...`);
        callback(["Artikel IoT", "Tips Laravel 11", "Mengenal Async JS"]);
    }, 1000);
}

// Eksekusi (Mulai terjadi penumpukan kode)
getUser(1, (user) => {
    getPosts(user.username, (posts) => {
        console.log("Hasil Akhir:", { user, posts });
    });
});


Kode Promise

// Mengubah fungsi menjadi berbasis Promise
function getUser(id) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("1. Mengambil data user dari database...");
            const success = true; // Simulasi kondisi
            if (success) {
                resolve({ id: id, username: "ahmad_muazim" });
            } else {
                reject("Gagal mengambil data user.");
            }
        }, 1500);
    });
}

function getPosts(username) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`2. Mengambil artikel milik ${username}...`);
            resolve(["Artikel IoT", "Tips Laravel 11", "Mengenal Async JS"]);
        }, 1000);
    });
}

// Eksekusi menggunakan Promise Chaining (.then & .catch)
getUser(1)
    .then((user) => getPosts(user.username))
    .then((posts) => {
        console.log("Hasil Akhir dengan Promise:", posts);
    })
    .catch((error) => {
        console.error("Terjadi Error:", error);
    });