Dùng AbortController — cách duy nhất huỷ được fetch đang chạy.
Bản ngắn gọn là AbortSignal.timeout():
js
const res = await fetch(url, { signal: AbortSignal.timeout(5000) })Khi cần huỷ thủ công (user rời trang, gõ query mới) thì tự quản controller:
js
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), 5000)
try {
const res = await fetch(url, { signal: controller.signal })
return await res.json()
} catch (err) {
if (err.name === 'AbortError' || err.name === 'TimeoutError') return null
throw err
} finally {
clearTimeout(timer) // tránh giữ timer khi request về sớm
}Cần vừa timeout vừa huỷ tay thì gộp signal bằng AbortSignal.any([controller.signal, AbortSignal.timeout(5000)]).
Các điểm hay bị hỏi thêm:
- Abort làm promise của fetch reject với AbortError, không phải resolve — phải bắt riêng, đừng báo lỗi cho user.
- Promise.race với một timer không huỷ request: kết nối vẫn chạy, vẫn tốn băng thông và vẫn có thể ghi state cũ. Chỉ AbortController mới dừng thật.
- Một signal dùng cho nhiều fetch sẽ huỷ tất cả cùng lúc — tiện khi rời trang.