Sitemap
Hannah Lin

Love coding ❤

Follow publication

Fetch: AbortController

7 min readApr 13, 2025

--

Data fetching 系列文

1. Data fetching and performance
2. Race condition
3. Fetch: AbortController

🔖 文章索引

1. The AbortController object
2. Using with fetch
3 In react
4. AbortController is scalable

AbortController 是原生的 JS Class,做的事就跟他的名字 Abort 一樣: 放棄已經開始的任務。這點在 asynchronous task 特別好用,因為原生 fetch 並沒有中途放棄的 method,所以恰好可以搭配 AbortController 。

The AbortController object

這個 controller 很簡單,有一個 abort 的 method,以及 signal property。當 abort() 被呼叫時

  • signal 觸發 abort event
  • signal.abortedfalsetrue
  • signal.reason 則從 undefinederror object ,當然你也可以自訂它
let controller = new AbortController();

// controller.abort() 執行後會觸發 controller.signal 的 abort event
controller.signal.addEventListener('abort', () => {
alert(controller.signal.reason)); // user cancellation
}

controller.abort('user cancellation'); // abort!

alert(controller.signal.aborted); // true

甚至可以運用在各種 event listeners 上,以前需要個別根據不同 listener removeEventListener() 但現在 single AbortController 就可以全部一次取消。

useEffect(() => {
const controller = new AbortController()

window.addEventListener('resize', handleResize, {
signal: controller.signal,
})
window.addEventListener('hashchange', handleHashChange, {
signal: controller.signal,
})
window.addEventListener('storage', handleStorageChange, {
signal: controller.signal,
})

return () => {
// Calling `.abort()` removes ALL event listeners
// associated with `controller.signal`. Gone!
controller.abort()
}
}, [])

Using with fetch

為了讓 fetch 可以中止 async task,需要把 signal property 放到 fetch option,這樣 fetch 就可以藉由 signal listen abort 這個 event

let controller = new AbortController();
fetch(url, {
signal: controller.signal
});

當執行 controller.abort(), fetch 藉由signal listen 到 abort 就會取消 請求,程式也會跳到 reject,所以可以在 catch 裡捕捉到 AbortError

// abort in 1 second
let controller = new AbortController();
setTimeout(() => controller.abort(), 1000);

let response = fetch('/article/fetch-abort/demo/hang', {
signal: controller.signal
})
.catch(err => {
if (err.name == 'AbortError') {
// handle abort()
} else {
throw err;
}
});

實際例子

  • Upload 一個檔案 (POST /upload),但當使用者按下 Cancel 就要中止上傳

In react

在中大型 React 專案中,若使用原生 fetch 來 request,可能會遇到 Race Condition 問題 (詳細例子會在 Race conditions 這篇講),而 React 每一次 render function 都是獨立的,所以常會發生難以預期結果,這時就很適合在 cleanup 時把此次的 fetch request 取消。

useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((r) => // xxx)

return () => {
// abort the request here
controller.abort();
};
}, [url]);

AbortSignal.timeout

timeout 這個屬性可以讓你甚至不用 AbortController 就可以做到若多久沒有成功就放棄 request

fetch(url, {
// Abort this request automatically if it takes
// more than 3000ms to complete.
signal: AbortSignal.timeout(3000),
})

AbortController is scalable

AbortController 也可以允許同時取消多個 async tasks,如以下範例,並且single controller 就可以做到

let urls = [...]; // a list of urls to fetch in parallel
let controller = new AbortController();

let ourJob = new Promise((resolve, reject) => { // our task
...
controller.signal.addEventListener('abort', reject);
});

// an array of fetch promises
let fetchJobs = urls.map(url => fetch(url, {
signal: controller.signal
}));

// Wait for fetches and our task in parallel
let results = await Promise.all([...fetchJobs, ourJob]);

// if controller.abort() is called from anywhere,
// it aborts all fetches and ourJob

--

--

Hannah Lin
Hannah Lin

Written by Hannah Lin

A Front End Developer girl comes from Taiwan

No responses yet