Cara Menggunakan Async/Await JavaScript untuk Handle API Tanpa Ribet

Bayangkan kamu sedang memesan kopi di kafe yang ramai. Setelah membayar, kasir memberi sebuah alat kecil bernama buzzer. Sembari menunggu kopi diracik oleh barista, kamu tidak perlu berdiri kaku di depan kasir. Kamu bisa duduk, bermain ponsel, atau membaca buku. Begitu buzzer bergetar, kamu tahu kopimu sudah siap dan kamu bisa mengambilnya.

Di dunia pemrograman, proses menunggu tanpa menghentikan aktivitas lain ini disebut dengan asynchronous programming. Di JavaScript, fitur terbaik untuk menangani proses ini dengan nyaman adalah async/await. Fitur ini membuat kode asynchronous yang biasanya membingungkan menjadi terlihat dan terasa seperti kode synchronous (berurutan) yang biasa kamu tulis.


Mengapa Async/Await Lahir?

Sebelum melompat ke sintaks async/await, kamu perlu tahu alasan mengapa fitur ini diciptakan. JavaScript secara kodrat berjalan pada satu jalur (single-threaded). Artinya, JavaScript hanya bisa melakukan satu hal dalam satu waktu. Jika ada proses yang memakan waktu lama, seperti mengambil data dari server atau mengunduh gambar besar, seluruh halaman web bisa membeku jika tidak ditangani dengan benar.

Tragedi Callback Hell

Dulu, developer menggunakan fungsi bernama callback untuk menangani proses asynchronous. Masalah muncul ketika sebuah proses membutuhkan data dari proses sebelumnya, yang juga membutuhkan data dari proses sebelumnya lagi. Kode akan mulai bergeser ke kanan dan membentuk struktur seperti piramida. Kondisi ini sering disebut dengan Callback Hell atau Pyramid of Doom.

// Contoh Callback Hell yang bikin pusing
ambilDataUser(function(user) {
    ambilDataKeranjang(user.id, function(keranjang) {
        hitungTotalBelanja(keranjang, function(total) {
            prosesPembayaran(total, function(status) {
                console.log("Pembayaran selesai: " + status);
            });
        });
    });
});

Kode di atas sangat sulit dibaca, susah dirawat, dan rentan sekali memicu bug saat kamu ingin menambahkan logika baru.

Solusi Setengah Matang: Promises

Untuk menyelamatkan developer dari Callback Hell, JavaScript memperkenalkan Promise pada ECMAScript 2015 (ES6). Kode menjadi lebih bersih karena menggunakan metode berantai (chaining) dengan fungsi .then() dan .catch().

// Contoh penggunaan Promise Chaining
ambilDataUser()
    .then(user => ambilDataKeranjang(user.id))
    .then(keranjang => hitungTotalBelanja(keranjang))
    .then(total => prosesPembayaran(total))
    .then(status => console.log("Pembayaran selesai: " + status))
    .catch(error => console.error("Terjadi kesalahan:", error));

Meskipun jauh lebih rapi, struktur ini tetap memiliki kelemahan. Ketika alur aplikasi semakin kompleks, barisan .then() yang terlalu panjang tetap membuat mata lelah dan logika pencabangan (seperti if-else) di dalamnya menjadi rumit.


Mengenal Konsep Dasar Async/Await

Hingga akhirnya pada ES2017 (ES8), muncullah async/await. Ini bukan teknologi baru yang mengubah cara kerja JavaScript, melainkan sebuah syntactic sugar. Artinya, di balik layar, async/await tetap menggunakan Promise, tetapi ditulis dengan gaya yang jauh lebih manusiawi dan ramah dibaca.

Aturan mainnya sangat sederhana dan melibatkan dua kata kunci utama:

  • async: Digunakan untuk mendeklarasikan bahwa sebuah fungsi berjalan secara asynchronous.
  • await: Digunakan di dalam fungsi async untuk menghentikan sementara eksekusi kode sampai Promise selesai diproses (baik sukses maupun gagal).

Cara Mengubah Fungsi Biasa Menjadi Fungsi Async

Kamu hanya perlu menambahkan kata kunci async di depan deklarasi fungsi. Fungsi yang diberi label async akan selalu mengembalikan sebuah Promise secara otomatis.

// Fungsi biasa
function sapa() {
    return "Halo, selamat belajar!";
}

// Fungsi Async
async function sapaAsync() {
    return "Halo, selamat belajar!";
}

console.log(sapa());      // Output: "Halo, selamat belajar!"
console.log(sapaAsync()); // Output: Promise { <fulfilled>: "Halo, selamat belajar!" }

Karena sapaAsync() mengembalikan Promise, kamu bisa mengambil nilainya menggunakan .then() atau mengonsumsinya dengan kata kunci await di dalam fungsi async lainnya.


Cara Menggunakan Kata Kunci Await

Kata kunci await bertugas sebagai lampu merah otomatis. Dia memberi tahu JavaScript: "Tunggu sebentar di baris ini sampai proses mengambil data selesai, baru jalankan baris berikutnya."

Mari kita lihat perbandingan nyata saat mengambil data dari API publik menggunakan fungsi fetch.

Gaya Lama Menggunakan Promise `.then()`

function dapatkanDataProduk() {
    fetch('https://fakestoreapi.com/products/1')
        .then(response => response.json())
        .then(data => console.log("Data Produk:", data))
        .catch(err => console.error(err));
}

dapatkanDataProduk();

Gaya Baru Menggunakan Async/Await

async function dapatkanDataProdukModern() {
    // Menunggu respon dari server
    const response = await fetch('https://fakestoreapi.com/products/1');
    
    // Menunggu konversi format data ke JSON
    const data = await response.json();
    
    console.log("Data Produk Modern:", data);
}

dapatkanDataProdukModern();

Perhatikan betapa bersihnya fungsi kedua. Tidak ada struktur bersarang (nesting). Kode dibaca lurus dari atas ke bawah, persis seperti kode synchronous, namun tetap mempertahankan performa asynchronous yang tidak memblokir aplikasi.

Catatan Penting: Kamu tidak bisa menggunakan kata kunci await di dalam fungsi biasa. Jika kamu nekat melakukannya, JavaScript akan protes dan menampilkan pesan error SyntaxError: await is only valid in async functions.
hr/

Strategi Error Handling pada Async/Await

Dalam aplikasi nyata, tidak semua permintaan ke server berjalan mulus. Koneksi internet bisa terputus, server bisa tumbang, atau URL API yang kamu tuju bisa saja salah ketik. Oleh karena itu, penanganan kesalahan (error handling) hukumnya wajib.

Pada Promise biasa, kita menggunakan .catch(). Pada async/await, kita kembali menggunakan blok standar yang sudah sangat familier di dunia pemrograman: try...catch.

Menggunakan Struktur Try...Catch

Caranya, bungkus semua baris kode yang berpotensi memicu error ke dalam blok try. Jika sesuatu berjalan salah, JavaScript akan langsung melompat ke blok catch tanpa menjalankan sisa kode di dalam blok try.

async function ambilDataUserAman() {
    try {
        // Sengaja menyalakan URL salah untuk memicu error
        const response = await fetch('https://fakestoreapi.com/situs-salah-ini');
        
        if (!response.ok) {
            throw new Error(`Gagal mengambil data! Status: ${response.status}`);
        }
        
        const data = await response.json();
        console.log(data);
    } catch (error) {
        // Semua error akan mendarat di sini
        console.error("Terjadi error sistem:", error.message);
    } finally {
        console.log("Proses selesai dijalankan.");
    }
}

ambilDataUserAman();

Blok finally di bagian akhir bersifat opsional. Blok ini akan selalu dijalankan, baik prosesmu berhasil maupun gagal. Biasanya, blok ini sangat berguna untuk mematikan indikator loading di aplikasi web kamu agar tidak berputar selamanya saat server error.


Mengoptimalkan Performa dengan Promise.all

Satu jebakan Batman yang sering dialami oleh developer saat baru belajar async/await adalah menulis kode yang terlalu lambat akibat efek "antrean berantai".

Bayangkan kamu ingin mengambil data profil user, data pesanan, dan data rekomendasi produk secara bersamaan. Ketiga data ini tidak saling bergantung satu sama lain.

Menunggu Satu Per Satu (Urutan Sekuensial)

async function ambilSemuaDataLambat() {
    const waktuMulai = Date.now();

    const user = await fetch('https://fakestoreapi.com/users/1').then(res => res.json());
    const produk = await fetch('https://fakestoreapi.com/products/1').then(res => res.json());
    const kartu = await fetch('https://fakestoreapi.com/carts/1').then(res => res.json());

    console.log(`Selesai dalam ${Date.now() - waktuMulai} milidetik`);
}

Jika masing-masing API membutuhkan waktu 1 detik, total waktu tunggu fungsi di atas adalah 3 detik. Ini tidak efisien karena proses kedua harus menunggu proses pertama selesai, padahal mereka tidak ada hubungannya.

Berjalan Bersamaan (Paralel) dengan Promise.all

Untuk menghemat waktu, kamu bisa melepas ketiga permintaan tersebut secara bersamaan di waktu yang sama menggunakan Promise.all.

async function ambilSemuaDataCepat() {
    const waktuMulai = Date.now();

    try {
        // Memicu ketiga fetch berjalan secara paralel
        const [user, produk, kartu] = await Promise.all([
            fetch('https://fakestoreapi.com/users/1').then(res => res.json()),
            fetch('https://fakestoreapi.com/products/1').then(res => res.json()),
            fetch('https://fakestoreapi.com/carts/1').then(res => res.json())
        ]);

        console.log("Data berhasil digabungkan");
        console.log(`Selesai dalam ${Date.now() - waktuMulai} milidetik`);
    } catch (error) {
        console.error("Salah satu request gagal!", error);
    }
}

Dengan taktik ini, total waktu tunggu terpangkas drastis menjadi hanya sekitar 1 detik (mengikuti waktu tunggu dari proses yang paling lambat). Aplikasi kamu pun menjadi berkali-kali lipat lebih responsif.


Tips Praktis Menggunakan Async/Await

Agar kodemu tidak hanya berjalan, tetapi juga bersih dan mudah dipahami oleh tim developer lain, terapkan tips praktis berikut:

  1. Gunakan Arrow Function dengan Tepat: Kamu juga bisa membuat fungsi async menggunakan gaya arrow function yang ringkas.
    const ambilData = async () => { ... };
  2. Hati-hati Saat Menggunakan Array Loop: Menggunakan .forEach() bersama async/await sering kali memunculkan perilaku aneh karena .forEach() tidak peduli dengan Promise. Sebagai gantinya, gunakan struktur loop for...of atau kombinasikan .map() dengan Promise.all.
  3. Jangan Menimbun Blok Try-Catch Berlebihan: Jika kamu punya banyak fungsi async, membungkus setiap baris dengan try-catch akan membuat kodemu kotor lagi. Solusinya, buatlah satu fungsi global error handler atau gunakan fungsi pembantu (helper) untuk mengelola error di satu tempat pusat.

Kesimpulan

Fitur async/await adalah lompatan besar dalam cara kita menulis kode asynchronous di JavaScript. Dengan menyulap struktur Promise yang berantai menjadi blok kode linier yang rapi, kamu bisa menulis logika aplikasi yang kompleks tanpa perlu takut terjebak dalam Callback Hell. Kuncinya adalah ingat untuk selalu menyematkan kata kunci async pada fungsi, manfaatkan blok try...catch untuk menjaga stabilitas aplikasi, dan gunakan Promise.all saat kamu butuh performa kecepatan maksimal.

Sekarang, giliran kamu mencoba mempraktikkannya langsung pada proyek aplikasi web yang sedang kamu bangun!


Daftar Referensi
  1. MDN Web Docs. Making asynchronous programming easier with async and await. Diakses pada Juni 2026, dari https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await
  2. JavaScript Info. Async/await. Diakses pada Juni 2026, dari https://javascript.info/async-await
  3. Node.js Berita Resmi. Asynchronous Flow Control. Diakses pada Juni 2026, dari https://nodejs.org/en/learn/asynchronous-work/asynchronous-flow-control
Ruang Debug
Belajar async/await JavaScript dari dasar sampai error handling. Bikin kode asynchronous lebih rapi, cepat, dan mudah dibaca tanpa callback hell.

Komentar