Konteks dan arahan
Halaman pencarian secara bersamaan meminta saran kata kunci, data profil, dan hasil pencarian. Kueri baru atau navigasi harus segera membatalkan pekerjaan lama; request jaringan yang terhenti harus mengalami batas waktu (timeout), tetapi kembali dari latar belakang tidak boleh menghitung waktu saat halaman dibekukan sebagai waktu request online. Rancang pembatalan dengan AbortSignal.timeout(), AbortSignal.any(), dan AbortController.
Topik ini cocok untuk wawancara frontend, performa web, dan arsitektur asinkron. Kuncinya adalah memisahkan pembatalan oleh pengguna, timeout, siklus hidup halaman, dan kegagalan jaringan yang sebenarnya, alih-alih menampilkan setiap pengecualian (exception) sebagai “request timed out.”
Apa yang sedang diuji oleh pewawancara
Jawaban yang kuat menyatakan bahwa AbortSignal.timeout() mengembalikan signal yang dibatalkan secara otomatis dengan TimeoutError; pembatalan oleh pengguna atau controller biasanya menghasilkan AbortError. Jawaban tersebut juga menjelaskan semantik active time: timer dijeda saat halaman berada di bfcache atau Worker sedang disuspensi. Beberapa signal dapat digabungkan dengan AbortSignal.any() sambil tetap mempertahankan alasan yang dapat didiagnosis.
Pertanyaan klarifikasi yang perlu diajukan terlebih dahulu
- Request mana yang aman untuk dibatalkan, dan apakah operasi penulisan (writes) harus diselesaikan atau ditanyakan statusnya nanti?
- Apakah timeout dimulai sejak tindakan pengguna, pengiriman request, atau visibilitas halaman?
- Apakah pembatalan oleh pengguna, unmount, navigasi, dan timeout memerlukan pesan yang berbeda?
- Apakah browser target mendukung metode statis ini, dan apakah fallback harus mempertahankan timer manual?
- Bagaimana penanganan retry, deduplikasi, caching, dan race condition pada hasil?
Kerangka jawaban 30 detik
“Untuk setiap request, saya akan menggabungkan pembatalan oleh pengguna dengan AbortSignal.timeout() dan mengklasifikasikan TimeoutError, AbortError, serta error jaringan secara terpisah. Batas waktu menggunakan active time, sehingga penangguhan (suspension) atau bfcache tidak dilaporkan sebagai latensi online. Kueri baru akan membatalkan controller lama, dan pengiriman hasil juga memeriksa urutan request atau signal agar data lama tidak menimpa data baru. Browser lama akan mendapatkan fallback kombinasi controller dan timer, yang diuji untuk pembatalan, timeout, pemulihan (resume), dan race condition.”
Jawaban mendalam langkah demi langkah
Langkah 1: Pisahkan tiga sumber pembatalan
AbortController.abort() diinisiasi oleh aplikasi; AbortSignal.timeout(ms) membatalkan saat active time mencapai batas; AbortSignal.any([...]) terpicu saat salah satu signal input dibatalkan. Semuanya dapat diteruskan ke fetch, tetapi lapisan logika bisnis harus mempertahankan alasannya agar tindakan meninggalkan halaman tidak dicatat sebagai kegagalan layanan.
const controller = new AbortController();
const timeout = AbortSignal.timeout(5000);
const signal = AbortSignal.any([controller.signal, timeout]);
fetch(url, { signal });Langkah 2: Klasifikasikan berdasarkan alasan
Timeout menggunakan DOMException TimeoutError; pembatalan oleh pengguna atau penghentian browser umumnya menggunakan AbortError. Masalah DNS, reset koneksi, dan CORS dapat muncul sebagai error lainnya. Kode penanganan exception harus memeriksa signal.reason atau nama exception sebelum menentukan pesan, retry, dan tingkat keparahan log.
Langkah 3: Pahami active time
timeout() mengukur active time alih-alih waktu jam dinding (wall clock) biasa. Dokumentasi mencatat bahwa timer dijeda saat halaman berada di bfcache atau Worker sedang disuspensi. Hal ini sesuai dengan jendela request aktif yang dirasakan pengguna, bukan batas waktu server yang absolut; server tetap memerlukan kebijakan timeout dan idempotensi tersendiri.
Langkah 4: Tangani race condition pada pencarian
Saat pengguna mengetik berulang kali, batalkan controller lama sebelum membuat signal untuk kueri baru. Meskipun request jaringan lama telah selesai, callback-nya mungkin masih berada dalam antrean; periksa urutan request, kueri saat ini, atau signal.aborted sebelum menerapkan perubahan state. Pembatalan saja tidak menjamin pengurutan versi hasil.
Langkah 5: Pisahkan pembacaan dan penulisan yang dapat dibatalkan
Pembacaan untuk pencarian, gambar, dan saran biasanya dapat dibatalkan. Operasi penulisan seperti pembayaran, pesanan, atau unggahan mungkin sudah diproses di server. Membatalkan penantian di sisi klien tidak membatalkan efek samping di sisi server. Gunakan kunci idempotensi (idempotency keys), kueri status, atau background task untuk operasi penulisan, alih-alih hanya memilih batas waktu yang lebih pendek.
Langkah 6: Rancang masa pakai (lifetime) signal
Batalkan controller komponen saat di-unmount, gunakan kembali signal tingkat halaman pada navigasi, dan tambahkan timeout request untuk operasi individual. Jangan pernah membagikan signal global yang telah dibatalkan secara permanen ke request berikutnya; buat signal baru untuk setiap operasi. Berikan alasan eksplisit untuk sumber yang berbeda saat informasi diagnostik diperlukan.
Langkah 7: Sediakan fallback untuk kompatibilitas
Jika browser lama tidak memiliki AbortSignal.timeout atau any, kombinasikan AbortController dengan setTimeout, bersihkan timer, dan lakukan normalisasi alasan. Deteksi fitur harus dilakukan pada saat runtime, dan jalur default tetap harus dapat menangani lingkungan tanpa metode statis tersebut.
Langkah 8: Verifikasi siklus hidup dan pembersihan (cleanup)
Uji input cepat, unmount, navigasi, penangguhan di latar belakang, pemulihan dari bfcache, timeout, pembatalan oleh pengguna, kegagalan jaringan, dan percobaan ulang yang berulang. Pastikan bahwa request dibatalkan, timer dibersihkan, hasil lama tidak diterapkan ke state, pesan akurat, dan penangkapan exception tidak menimbulkan promise yang tidak tertangani atau peringatan pembaruan state.
Pertimbangan dan batasan
Penggabungan signal mengurangi kode penghubung pembatalan, tetapi tidak membuat transaksi server dapat dibatalkan kembali atau menjamin respons tidak akan pernah tiba. Semantik active time cocok untuk pengalaman pengguna tetapi bukan untuk SLA end-to-end. Tentukan nilai timeout berdasarkan jenis request, kondisi jaringan, dan batas percobaan ulang, serta koordinasikan dengan batas waktu server.
Jangan hanya menggunakan Promise.race untuk menyimulasikan timeout sementara membiarkan request yang mendasarinya tetap berjalan. Teruskan AbortSignal agar jaringan dan sumber daya dapat dihentikan. Jangan mencatat setiap pembatalan sebagai error, karena navigasi normal akan mencemari peringatan pemantauan.
Rencana peluncuran dan bukti verifikasi
Mulailah dengan mesin status (state machine) request untuk pembacaan pencarian: buat signal, kirim request, klasifikasikan alasan, terapkan hasil yang memiliki versi, dan batalkan saat unmount. Catat jenis request, alasan, durasi aktif, jumlah percobaan ulang, dan status akhir tanpa menyertakan teks kueri yang sensitif.
Bandingkan browser yang didukung dan browser dengan fallback di seluruh skenario bfcache, Workers, jaringan lambat, dan input cepat. Kriteria penerimaan mencakup tidak adanya penimpaan hasil lama, tidak ada kebocoran timer, pesan pembatalan yang akurat, tidak ada pembatalan penulisan yang tidak disengaja, serta adanya bukti idempotensi server atau kueri status.
Jebakan umum dan pertanyaan lanjutan
Menyamakan TimeoutError dan AbortError
Pembatalan oleh pengguna, unmount, dan timeout memiliki langkah tindak lanjut yang berbeda. Klasifikasikan berdasarkan alasan untuk menentukan pesan, pencatatan log, dan percobaan ulang.
Menganggap pembatalan klien membatalkan penulisan di server
Request mungkin sudah sampai di server. Operasi penulisan memerlukan kunci idempotensi, kueri status, atau alur kompensasi.
Mengganti pembatalan nyata dengan Promise.race
Penggunaan race hanya mengubah apa yang ditunggu oleh pemanggil, tetapi tidak menghentikan proses fetch yang mendasarinya. Teruskan AbortSignal dan bersihkan sumber daya.
Mengabaikan active time terkait bfcache
Timeout dapat dijeda saat halaman disuspensi. Jangan menganggap durasi setelah pemulihan sebagai SLA request online.
Bagaimana cara mencegah hasil lama menimpa hasil baru?
Membatalkan controller lama saja tidak cukup. Bandingkan urutan kueri, versi, atau kueri saat ini sebelum menerapkan perubahan state agar urutan kedatangan respons tidak mengubah state secara salah.