導入:なぜRequest.prototype.signalが必要なのか
フロントエンド開発において、通信処理は避けて通れません。しかし、ユーザーがページを離れたり、古い検索結果を破棄して新しいリクエストを優先したい場合に、進行中のFetch処理を放置するのはリソースの無駄です。今回解説する「Request.prototype.signal」は、AbortControllerと連携し、実行中のHTTPリクエストを安全かつ効率的にキャンセルするための重要なインターフェースです。これを活用することで、アプリケーションの応答性とパフォーマンスが大きく向上します。
基礎知識:AbortControllerとsignalの仕組み
まず理解しておくべきは、AbortControllerというWeb APIの存在です。これは、DOM操作やネットワークリクエストといった非同期処理を中止するためのシグナルを送るコントローラーです。
このコントローラーには「signal」プロパティがあり、これが「中断の合図」を運ぶ役割を担います。Fetch APIのRequestオブジェクトやfetch関数は、このsignalを受け取ることで「中断命令が来たら即座に通信を終了する」という挙動を実装できます。
実装:Request.prototype.signalの活用手順
実装の流れは非常にシンプルです。
1. AbortControllerインスタンスを作成する。
2. そのsignalプロパティをRequestの初期化オプション、またはfetch関数の引数に渡す。
3. 中断したいタイミング(例えば「別のボタンが押されたとき」や「コンポーネントがアンマウントされたとき」)で、コントローラーのabortメソッドを呼び出す。
サンプルプログラム:キャンセル可能なFetch実装
以下は、ボタン押下で通信を強制キャンセルする実用的なコード例です。
// 1. コントローラーの初期化
const controller = new AbortController();
const { signal } = controller;
async function fetchData() {
try {
// 2. Requestオブジェクトの作成時にsignalを渡す
const request = new Request('https://api.example.com/data', { signal });
// 3. fetchでリクエストを実行
const response = await fetch(request);
const data = await response.json();
console.log('取得成功:', data);
} catch (error) {
// 4. キャンセルされた場合はAbortErrorが発生する
if (error.name === 'AbortError') {
console.log('リクエストはキャンセルされました');
} else {
console.error('通信エラー:', error);
}
}
}
// 実行開始
fetchData();
// 5. 特定の条件下でリクエストをキャンセルする
// ここでは、開始から1秒後に強制終了させる例
setTimeout(() => {
controller.abort();
}, 1000);
応用・注意点:現場で陥りやすい罠
実務でこの機能を導入する際、以下の点に注意してください。
1. エラーハンドリングを忘れずに
キャンセルが発生すると、fetchは例外(AbortError)を投げます。try-catchブロックで必ずこのエラーを捕捉し、ユーザーに「中断しました」という適切なメッセージを表示するか、ログを適切に処理してください。
2. 複数のリクエストに使い回さない
AbortControllerのインスタンスは一度abortすると再利用できません。新しいリクエストを行うたびに、必ず新しいインスタンスを作成する設計にしましょう。
3. Reactでの利用例
ReactのuseEffect内でfetchを行う場合は、クリーンアップ関数内でabortを呼ぶのが定石です。これにより、コンポーネントがアンマウントされた瞬間に通信を停止させることができ、メモリリークや不要な状態更新(未マウント状態でのsetState呼び出し)を防ぐことができます。
適切にsignalを管理することで、ユーザーにとってストレスのない、堅牢な通信レイヤーを構築してください。