Pelajari cara menggunakan AbortController JavaScript untuk membatalkan operasi asinkron seperti permintaan fetch, timer, dan lainnya secara efektif, memastikan kode yang lebih bersih dan berperforma.
JavaScript AbortController: Menguasai Pembatalan Operasi Asinkron
Dalam pengembangan web modern, operasi asinkron ada di mana-mana. Mengambil data dari API, mengatur timer, dan menangani interaksi pengguna sering kali melibatkan kode yang berjalan secara independen dan berpotensi untuk durasi yang lama. Namun, ada skenario di mana Anda perlu membatalkan operasi ini sebelum selesai. Di sinilah antarmuka AbortController
di JavaScript datang untuk menyelamatkan. Ini menyediakan cara yang bersih dan efisien untuk mengirim sinyal permintaan pembatalan ke operasi DOM dan tugas asinkron lainnya.
Memahami Kebutuhan Pembatalan
Sebelum mendalami detail teknis, mari kita pahami mengapa pembatalan operasi asinkron itu penting. Pertimbangkan skenario umum berikut:
- Navigasi Pengguna: Seorang pengguna memulai kueri pencarian, memicu permintaan API. Jika mereka dengan cepat menavigasi ke halaman yang berbeda sebelum permintaan selesai, permintaan asli menjadi tidak relevan dan harus dibatalkan untuk menghindari lalu lintas jaringan yang tidak perlu dan efek samping yang potensial.
- Manajemen Waktu Tunggu: Anda menetapkan waktu tunggu untuk operasi asinkron. Jika operasi selesai sebelum waktu tunggu berakhir, Anda harus membatalkan waktu tunggu tersebut untuk mencegah eksekusi kode yang berlebihan.
- Pelepasan Komponen (Component Unmounting): Dalam kerangka kerja front-end seperti React atau Vue.js, komponen sering membuat permintaan asinkron. Ketika sebuah komponen dilepas (unmount), setiap permintaan yang sedang berlangsung yang terkait dengan komponen tersebut harus dibatalkan untuk menghindari kebocoran memori dan kesalahan yang disebabkan oleh pembaruan komponen yang sudah dilepas.
- Keterbatasan Sumber Daya: Di lingkungan dengan sumber daya terbatas (misalnya, perangkat seluler, sistem tertanam), membatalkan operasi yang tidak perlu dapat membebaskan sumber daya berharga dan meningkatkan kinerja. Misalnya, membatalkan unduhan gambar besar jika pengguna menggulir melewati bagian halaman tersebut.
Memperkenalkan AbortController dan AbortSignal
Antarmuka AbortController
dirancang untuk menyelesaikan masalah pembatalan operasi asinkron. Ini terdiri dari dua komponen kunci:
- AbortController: Objek ini mengelola sinyal pembatalan. Ia memiliki satu metode,
abort()
, yang digunakan untuk memberi sinyal permintaan pembatalan. - AbortSignal: Objek ini mewakili sinyal bahwa suatu operasi harus dibatalkan. Ini terkait dengan
AbortController
dan diteruskan ke operasi asinkron yang perlu dapat dibatalkan.
Penggunaan Dasar: Membatalkan Permintaan Fetch
Mari kita mulai dengan contoh sederhana membatalkan permintaan fetch
:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});
// To cancel the fetch request:
controller.abort();
Penjelasan:
- Kita membuat instance
AbortController
. - Kita mendapatkan
AbortSignal
yang terkait daricontroller
. - Kita meneruskan
signal
ke opsifetch
. - Jika kita perlu membatalkan permintaan, kita memanggil
controller.abort()
. - Di blok
.catch()
, kita memeriksa apakah kesalahannya adalahAbortError
. Jika ya, kita tahu bahwa permintaan telah dibatalkan.
Menangani AbortError
Ketika controller.abort()
dipanggil, permintaan fetch
akan ditolak dengan AbortError
. Sangat penting untuk menangani kesalahan ini dengan tepat dalam kode Anda. Kegagalan untuk melakukannya dapat menyebabkan penolakan promise yang tidak ditangani dan perilaku yang tidak terduga.
Berikut adalah contoh yang lebih kuat dengan penanganan kesalahan:
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
return null; // Or throw the error to be handled further up
} else {
console.error('Fetch error:', error);
throw error; // Re-throw the error to be handled further up
}
}
}
fetchData();
// To cancel the fetch request:
controller.abort();
Praktik Terbaik untuk Menangani AbortError:
- Periksa nama kesalahan: Selalu periksa apakah
error.name === 'AbortError'
untuk memastikan Anda menangani jenis kesalahan yang benar. - Kembalikan nilai default atau lempar ulang: Tergantung pada logika aplikasi Anda, Anda mungkin ingin mengembalikan nilai default (misalnya,
null
) atau melempar ulang kesalahan untuk ditangani lebih lanjut di tumpukan panggilan. - Bersihkan sumber daya: Jika operasi asinkron mengalokasikan sumber daya apa pun (misalnya, timer, event listener), bersihkan sumber daya tersebut di penangan
AbortError
.
Membatalkan Timer dengan AbortSignal
AbortSignal
juga dapat digunakan untuk membatalkan timer yang dibuat dengan setTimeout
atau setInterval
. Ini memerlukan sedikit lebih banyak pekerjaan manual, karena fungsi timer bawaan tidak secara langsung mendukung AbortSignal
. Anda perlu membuat fungsi kustom yang mendengarkan sinyal pembatalan dan membersihkan timer saat sinyal tersebut dipicu.
function cancellableTimeout(callback, delay, signal) {
let timeoutId;
const timeoutPromise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
resolve(callback());
}, delay);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new Error('Timeout Aborted'));
});
});
return timeoutPromise;
}
const controller = new AbortController();
const signal = controller.signal;
cancellableTimeout(() => {
console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));
// To cancel the timeout:
controller.abort();
Penjelasan:
- Fungsi
cancellableTimeout
menerima callback, penundaan (delay), danAbortSignal
sebagai argumen. - Fungsi ini menyiapkan
setTimeout
dan menyimpan ID timeout. - Fungsi ini menambahkan event listener ke
AbortSignal
yang mendengarkan eventabort
. - Ketika event
abort
dipicu, event listener akan membersihkan timeout dan menolak promise.
Membatalkan Event Listener
Mirip dengan timer, Anda dapat menggunakan AbortSignal
untuk membatalkan event listener. Ini sangat berguna ketika Anda ingin menghapus event listener yang terkait dengan komponen yang sedang dilepas (unmounted).
const controller = new AbortController();
const signal = controller.signal;
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
}, { signal });
// To cancel the event listener:
controller.abort();
Penjelasan:
- Kita meneruskan
signal
sebagai opsi ke metodeaddEventListener
. - Ketika
controller.abort()
dipanggil, event listener akan dihapus secara otomatis.
AbortController dalam Komponen React
Di React, Anda dapat menggunakan AbortController
untuk membatalkan operasi asinkron saat komponen dilepas (unmount). Ini penting untuk mencegah kebocoran memori dan kesalahan yang disebabkan oleh pembaruan komponen yang sudah dilepas. Berikut adalah contoh menggunakan hook useEffect
:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setData(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
}
}
fetchData();
return () => {
controller.abort(); // Cancel the fetch request when the component unmounts
};
}, []); // Empty dependency array ensures this effect runs only once on mount
return (
{data ? (
Data: {JSON.stringify(data)}
) : (
Loading...
)}
);
}
export default MyComponent;
Penjelasan:
- Kita membuat
AbortController
di dalam hookuseEffect
. - Kita meneruskan
signal
ke permintaanfetch
. - Kita mengembalikan fungsi pembersihan (cleanup function) dari hook
useEffect
. Fungsi ini akan dipanggil saat komponen dilepas (unmount). - Di dalam fungsi pembersihan, kita memanggil
controller.abort()
untuk membatalkan permintaan fetch.
Kasus Penggunaan Lanjutan
Merangkai AbortSignal
Terkadang, Anda mungkin ingin merangkai beberapa AbortSignal
bersama-sama. Misalnya, Anda mungkin memiliki komponen induk yang perlu membatalkan operasi di komponen anaknya. Anda dapat mencapai ini dengan membuat AbortController
baru dan meneruskan sinyalnya ke komponen induk dan anak.
Menggunakan AbortController dengan Pustaka Pihak Ketiga
Jika Anda menggunakan pustaka pihak ketiga yang tidak secara langsung mendukung AbortSignal
, Anda mungkin perlu menyesuaikan kode Anda agar berfungsi dengan mekanisme pembatalan pustaka tersebut. Ini mungkin melibatkan pembungkusan fungsi asinkron pustaka dalam fungsi Anda sendiri yang menangani AbortSignal
.
Manfaat Menggunakan AbortController
- Peningkatan Kinerja: Membatalkan operasi yang tidak perlu dapat mengurangi lalu lintas jaringan, penggunaan CPU, dan konsumsi memori, yang mengarah pada peningkatan kinerja, terutama pada perangkat dengan sumber daya terbatas.
- Kode Lebih Bersih:
AbortController
menyediakan cara yang terstandarisasi dan elegan untuk mengelola pembatalan, membuat kode Anda lebih mudah dibaca dan dipelihara. - Pencegahan Kebocoran Memori: Membatalkan operasi asinkron yang terkait dengan komponen yang dilepas mencegah kebocoran memori dan kesalahan yang disebabkan oleh pembaruan komponen yang sudah dilepas.
- Pengalaman Pengguna yang Lebih Baik: Membatalkan permintaan yang tidak relevan dapat meningkatkan pengalaman pengguna dengan mencegah informasi usang ditampilkan dan mengurangi latensi yang dirasakan.
Kompatibilitas Browser
AbortController
didukung secara luas di browser modern, termasuk Chrome, Firefox, Safari, dan Edge. Anda dapat memeriksa tabel kompatibilitas di MDN Web Docs untuk informasi terbaru.
Polyfill
Untuk browser lama yang tidak mendukung AbortController
secara native, Anda dapat menggunakan polyfill. Polyfill adalah sepotong kode yang menyediakan fungsionalitas fitur baru di browser lama. Ada beberapa polyfill AbortController
yang tersedia secara online.
Kesimpulan
Antarmuka AbortController
adalah alat yang ampuh untuk mengelola operasi asinkron di JavaScript. Dengan menggunakan AbortController
, Anda dapat menulis kode yang lebih bersih, lebih berperforma, dan lebih kuat yang menangani pembatalan dengan baik. Baik Anda mengambil data dari API, mengatur timer, atau mengelola event listener, AbortController
dapat membantu Anda meningkatkan kualitas keseluruhan aplikasi web Anda.