JavaScript AbortController 撤銷非同步任務

在現代 JavaScript 開發中,處理非同步任務(如 API 請求)是家常便飯。然而,有時候我們會需要「取消」已經發出但尚未完成的任務——例如當使用者在切換頁面後,原本舊頁面的 API 請求就應該被中斷,以節省網路資源。

AbortController 正是為了解決這個需求而誕生的標準方法。

基本概念:Controller 與 Signal

AbortController 包含兩個核心部分:

  1. 實例 (The Controller): 它是發號施令的人,擁有一個 abort() 方法。
  2. 信號 (The Signal): 它是訊息傳遞者 (controller.signal),會被傳遞給想要監聽「取消指令」的非同步任務。

取消 Fetch 請求

這是 AbortController 最常見的應用場景。

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

fetch('https://api.example.com/data', { signal })
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((err) => {
    if (err.name === 'AbortError') {
      console.log('請求已被手動取消');
    } else {
      console.error('發生其他錯誤', err);
    }
  });

// 當你想取消請求時:
controller.abort();

自動超時中斷 (AbortSignal.timeout)

在 2024-2025 年的標準中,瀏覽器引入了更簡潔的超時處理方式:AbortSignal.timeout(ms)。它會自動在指定毫秒後發出取消信號。

// 設定 5 秒後自動超時取消
fetch('/long-api-call', { signal: AbortSignal.timeout(5000) }).catch((err) => {
  if (err.name === 'TimeoutError') {
    console.log('連線超時了');
  }
});

一次清除多個 Event Listeners

AbortController 不僅能用於 Fetch,還能用來清理事件監聽器。這在 Single Page Application (SPA) 中非常有用,能有效防止記憶體洩漏。

const controller = new AbortController();

window.addEventListener(
  'scroll',
  () => {
    console.log('捲動中...');
  },
  { signal: controller.signal }
);

window.addEventListener(
  'resize',
  () => {
    console.log('縮放中...');
  },
  { signal: controller.signal }
);

// 當元件卸載 (Unmount) 時,一併清除上述所有監聽器:
controller.abort();

同時監聽多個信號 (AbortSignal.any)

有時候一個任務可能因為「多種原因」而取消(例如:使用者點擊取消按鈕 超時),這時可以使用 AbortSignal.any()

const userCancel = new AbortController();
const timeoutSignal = AbortSignal.timeout(3000);

//只要其中一個信號觸發,請求就會中斷
fetch('/api', {
  signal: AbortSignal.any([userCancel.signal, timeoutSignal]),
});

實作自己的「可撤銷」函式

你也可以在自定義的異步函式或 Promise 中支援 AbortSignal。

function myTask(signal) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => resolve('完成!'), 5000);

    // 監聽外部傳來的取消事件
    signal.addEventListener('abort', () => {
      clearTimeout(timer);
      reject(new Error('任務被中止了'));
    });
  });
}

結語

AbortController 是目前 JavaScript 取消行為的正式標準。它不僅統一了 API 請求的中斷方式,還擴展到了事件管理等多個領域。學會使用它,能讓你的應用程式資源管理變得更加專業且優雅。

相關閱讀: