Permintaan jaringan yang sudah tidak dibutuhkan tetap bisa menghabiskan bandwidth, menambah beban server, dan membuat antarmuka menampilkan hasil yang keliru. Contohnya, pengguna mengetik kata kunci pencarian dengan cepat: permintaan untuk kata lama mungkin selesai setelah permintaan terbaru. Di JavaScript, AbortController menyediakan cara standar untuk membatalkan operasi yang mendukung AbortSignal, termasuk fetch().
Pembatalan bukan sekadar optimasi. Jika digunakan dengan tepat, mekanisme ini membantu menjaga antarmuka tetap responsif dan mengurangi pekerjaan yang tidak lagi berguna. Namun, ada batas penting: membatalkan permintaan di browser tidak selalu berarti server telah berhenti memproses pekerjaan yang telanjur diterimanya.
Apa Itu AbortController dan AbortSignal?
AbortController adalah objek yang dapat memicu pembatalan. Objek ini memiliki properti signal dan metode abort(). Signal tersebut diberikan kepada operasi yang ingin dikendalikan. Ketika abort() dipanggil, signal berubah menjadi aborted dan operasi yang mendukungnya dapat menghentikan pekerjaannya.
Pemisahan controller dan signal membuat kode lebih mudah dirawat: bagian yang memiliki controller menentukan kapan operasi dibatalkan, sementara fungsi yang menerima signal bertanggung jawab merespons pembatalan. Satu signal dapat dipakai untuk beberapa operasi yang memang perlu dihentikan bersama, misalnya mengambil data utama dan gambar pratinjau dalam satu layar.
Membatalkan fetch dengan aman
Contoh paling umum adalah membatalkan permintaan HTTP ketika komponen tidak lagi membutuhkan responsnya. Buat controller, teruskan signal ke fetch(), lalu panggil abort() pada saat yang sesuai.
const controller = new AbortController();
async function loadProfile() {
try {
const response = await fetch("/api/profile", {
signal: controller.signal
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === "AbortError") {
console.info("Permintaan dibatalkan");
return null;
}
throw error;
}
}
loadProfile();
// Panggil saat data tak lagi dibutuhkan:
controller.abort();Dalam aplikasi nyata, panggil controller.abort() dari aksi yang jelas, seperti tombol Batal, pergantian halaman, atau pembersihan komponen. Jangan langsung memanggilnya sebelum operasi sempat berjalan. Periksa juga response.ok; respons HTTP seperti 404 atau 500 biasanya tidak otomatis membuat fetch() melempar error.
Membuat timeout untuk permintaan lambat
Permintaan yang menunggu terlalu lama dapat membuat indikator pemuatan terus aktif. Salah satu pola yang kompatibel luas adalah menggabungkan controller dengan timer. Timer dibersihkan di blok finally agar tidak tertinggal setelah permintaan selesai.
const controller = new AbortController();
const timer = setTimeout(() => {
controller.abort();
}, 8000);
try {
const response = await fetch("/api/report", {
signal: controller.signal
});
if (!response.ok) throw new Error("Gagal mengambil laporan");
const report = await response.json();
console.log(report);
} catch (error) {
if (error.name === "AbortError") {
console.warn("Permintaan dibatalkan atau melewati batas waktu");
} else {
console.error(error);
}
} finally {
clearTimeout(timer);
}Contoh tersebut memperlakukan pembatalan manual dan timeout dengan cara yang sama. Jika antarmuka perlu membedakan keduanya, simpan alasan pembatalan secara terpisah atau gunakan kemampuan modern seperti AbortSignal.timeout() dan AbortSignal.any() setelah memastikan dukungan browser sasaran. Signal timeout modern dapat menghasilkan error bernama TimeoutError, sehingga penanganannya perlu disesuaikan.
Mencegah hasil pencarian lama menimpa hasil terbaru
Kolom pencarian langsung sering mengirim beberapa permintaan dalam waktu berdekatan. Tanpa kontrol, respons yang lebih lambat dari kata kunci lama bisa menggantikan hasil yang benar. Simpan controller aktif dan batalkan permintaan sebelumnya setiap kali pencarian baru dimulai.
let searchController;
async function searchProducts(query) {
searchController?.abort();
searchController = new AbortController();
try {
const response = await fetch(
`/api/search?q=${encodeURIComponent(query)}`,
{ signal: searchController.signal }
);
if (!response.ok) throw new Error("Pencarian gagal");
return await response.json();
} catch (error) {
if (error.name === "AbortError") return null;
throw error;
}
}Pola ini mengurangi pekerjaan yang tidak lagi relevan, tetapi tetap perlu menangani kondisi saat fungsi dipanggil dengan query kosong dan saat komponen sudah dilepas. Untuk aplikasi yang kompleks, debounce dapat mengurangi jumlah permintaan sebelum dikirim, sedangkan AbortController menghentikan permintaan yang sudah berjalan. Keduanya bisa dipakai bersama.
Hal yang perlu diperhatikan
- Tangani pembatalan secara eksplisit.
AbortErrorbiasanya kondisi normal ketika pengguna berpindah halaman, bukan selalu kegagalan aplikasi. - Jangan menelan semua error. Bedakan pembatalan dari masalah jaringan, respons HTTP yang gagal, dan kesalahan pemrosesan data.
- Jangan menganggap server pasti berhenti. Browser dapat berhenti menunggu atau membaca respons, tetapi server mungkin sudah menjalankan operasi. Endpoint yang mengubah data tetap memerlukan validasi, otorisasi, dan desain idempotensi bila sesuai.
- Periksa dukungan lingkungan.
AbortControllerdidukung luas pada browser modern, tetapi lingkungan lama atau runtime khusus mungkin memerlukan pengecekan kompatibilitas. - Batalkan pada waktu yang tepat. Controller yang digunakan ulang setelah dibatalkan tidak dapat diaktifkan kembali. Buat controller baru untuk operasi berikutnya.
Kesimpulan
AbortController memberi kendali yang sederhana atas permintaan asinkron: membatalkan fetch() yang tidak diperlukan, menetapkan batas waktu, dan mencegah hasil lama mengganggu antarmuka. Gunakan signal secara konsisten, bersihkan timer, serta tangani AbortError tanpa menyembunyikan error lain. Untuk pemahaman lebih mendalam, rujuk dokumentasi AbortController di MDN, AbortSignal, dan standar Fetch. Dengan pola ini, kode jaringan lebih mudah diprediksi dan pengalaman pengguna menjadi lebih baik.
