AbortSignal.timeout()でfetchに数行でタイムアウトを付ける(Node.js/ブラウザ共通)
fetch には既定のタイムアウトが無い。相手が応答を返さないと、その待ちは(TCP側のタイムアウトまで)延々と続く。従来は AbortController と setTimeout を手で組み合わせていたが、いまは AbortSignal.timeout() で数行にできる。Node.js(18以降)とモダンブラウザの両方で使える。
以前のやり方(手書き)
1 | |
動くが、clearTimeout の後始末が要る。忘れるとプロセスが余計に生き続けることがある。
いまのやり方:AbortSignal.timeout()
1 | |
これだけ。AbortSignal.timeout(ms) は「ms経過後に自動でabortするsignal」を返す。タイマーの後始末も内部でやってくれる。タイムアウトすると TimeoutError(name === "TimeoutError" の DOMException)で reject される。
1 | |
タイムアウトと「ユーザーによるキャンセル」を両立する
「タイムアウト」も付けたいし「ユーザーの中止ボタン」でも止めたい、というときは AbortSignal.any() で複数のsignalを合成する。どれか1つが発火したら中止になる。
1 | |
中止理由で分岐したいときは signal.reason を見る。AbortSignal.timeout() 由来なら TimeoutError、controller.abort(reason) なら渡した reason が入る。
注意点
- タイムアウトはヘッダ受信までではなく、fetchのabortが効く範囲。
res.bodyを後からゆっくり読む場合、本文読み取り中の停止は別途 signal を効かせる必要がある(res.text()はabortで中断される)。 - リトライと組み合わせるなら、タイムアウトごとに新しい
AbortSignal.timeout()を作る(signalは使い捨て)。 - Node.jsは18以降で
AbortSignal.timeout/anyが使える。古い環境向けには手書き版をフォールバックに。
まとめ
fetch のタイムアウトは、いまや AbortSignal.timeout(ms) を signal に渡すだけで済む。ユーザーキャンセルと併用したいときは AbortSignal.any() で合成する。AbortController + setTimeout + clearTimeout の定型を書いていたなら、後始末込みで数行に減らせる。
AbortSignal.timeout()でfetchに数行でタイムアウトを付ける(Node.js/ブラウザ共通)
https://blog.hashito.biz/2026/07/18/abortsignal-timeout-fetch-timeout-node-browser/