Skip to main content

AbortController介紹

一、什麼是 AbortController?

AbortController 是瀏覽器內建的一個 API,用來 控制取消操作
最常見的用途就是搭配 fetch,當你發送網路請求後,如果在某些情況下不需要回應(例如:使用者跳頁、搜尋框輸入新關鍵字等),就可以用它來「中止」請求,避免浪費網路資源或產生不必要的結果。


二、AbortController 的組成

  • AbortController
    這是一個「控制器」,可以建立「信號(signal)」並負責觸發取消事件。

  • AbortSignal
    這是「信號物件」,代表某個操作是否被中止。當你呼叫 controller.abort(),這個 signal 就會通知對應的操作要結束。

const controller = new AbortController();
const signal = controller.signal;

三、基本使用範例

1. 搭配 fetch

const controller = new AbortController();
const signal = controller.signal;

fetch("https://jsonplaceholder.typicode.com/posts", { signal })
.then(response => response.json())
.then(data => console.log("成功回應:", data))
.catch(err => {
if (err.name === "AbortError") {
console.log("請求已被中止");
} else {
console.error("其他錯誤:", err);
}
});

// 在 1 秒後中止請求
setTimeout(() => {
controller.abort();
}, 1000);

👉 說明:

  1. 建立 AbortController,並把 signal 傳給 fetch

  2. 如果 controller.abort() 被呼叫,fetch 就會丟出一個 AbortError

  3. 這樣就可以在特定情況下結束請求,避免繼續消耗資源。


2. 搭配事件監聽器

AbortController 不只可以用在 fetch,也能用在支援 AbortSignal 的 API,例如事件監聽器。

const controller = new AbortController();
const signal = controller.signal;

document.addEventListener("click", () => {
console.log("文件被點擊!");
}, { signal });

// 中止後事件監聽器會自動移除
setTimeout(() => {
controller.abort();
console.log("點擊事件監聽器已被移除");
}, 3000);

四、常見使用情境

  1. 搜尋框即時查詢
    使用者輸入新關鍵字時,取消舊的 API 請求,避免多餘的結果回來。

  2. 頁面跳轉
    如果使用者離開當前頁面,不需要再等待 API 回應,直接中止請求。

  3. 多重事件監聽管理
    利用 AbortSignal 可以更乾淨地管理監聽器,而不用自己手動呼叫 removeEventListener


五、總結

  • AbortController 提供 中止操作 的能力。

  • controller.signal 要傳給支援的 API(例如 fetch 或事件監聽器)。

  • 呼叫 controller.abort() 就會通知相關操作中止。

  • 常見於 取消請求清除事件監聽器,讓程式更有效率、更乾淨。

React Hooks 範例:搜尋框自動取消前一次的 fetch

import React, { useState, useEffect } from "react";

export default function SearchBox() {
const [query, setQuery] = useState(""); // 使用者輸入的關鍵字
const [results, setResults] = useState([]); // API 回傳的結果
const [loading, setLoading] = useState(false); // 是否正在載入中
const [error, setError] = useState(null); // 錯誤訊息

useEffect(() => {
if (!query) {
setResults([]);
return;
}

const controller = new AbortController(); // 建立控制器
const signal = controller.signal;

const fetchData = async () => {
try {
setLoading(true);
setError(null);

const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?q=${query}`,
{ signal }
);

if (!response.ok) {
throw new Error("網路回應失敗");
}

const data = await response.json();
setResults(data); // 更新結果(immutable 更新)
} catch (err) {
if (err.name === "AbortError") {
console.log("前一次的請求已被中止");
} else {
setError(err.message);
}
} finally {
setLoading(false);
}
};

fetchData();

// cleanup:在 effect 被下一次觸發時,中止前一次請求
return () => {
controller.abort();
};
}, [query]); // query 改變時才會觸發新的 fetch

return (
<div style={{ padding: "20px" }}>
<h2>搜尋文章</h2>
<input
type="text"
placeholder="輸入關鍵字..."
value={query}
onChange={(e) => setQuery(e.target.value)}
style={{ width: "300px", padding: "8px", fontSize: "16px" }}
/>
{loading && <p>載入中...</p>}
{error && <p style={{ color: "red" }}>錯誤:{error}</p>}
<ul>
{results.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</div>
);
}


程式重點解析

  1. 每次輸入 query 改變時,useEffect 都會重新觸發 → 開始一個新的 fetch

  2. cleanup 機制 (return () => controller.abort()) → 保證舊的請求會被取消。

  3. 錯誤判斷 → 只有 AbortError 才忽略,其他錯誤要顯示給使用者。

  4. immutable 更新setResults(data) 直接建立新的陣列,避免修改舊的 state。

  5. 狀態切分loadingerrorresults 都分開 state,讓 UI 乾淨。