多くの開発者がAbortController
を理解していると思っているかもしれませんが、その機能は基本的な用途をはるかに超えています。たとえば、fetch
リクエストのキャンセル、イベントリスナーの管理、Reactフックとの連携などです。
本当にAbortController
の威力を知っていますか?それでは見てみましょう。
AbortControllerを使ったfetch
リクエストのキャンセル
AbortController
をfetch
と一緒に使うことは、最も一般的な用途です。
以下は、AbortController
を使ってキャンセル可能なfetch
リクエストを作成する例です:
fetchButton.onclick = async () => {
const controller = new AbortController();
// キャンセルボタンを追加
abortButton.onclick = () => controller.abort();
try {
const response = await fetch('/json', { signal: controller.signal });
const data = await response.json();
// ビジネスロジックをここで処理
} catch (error) {
const isUserAbort = error.name === 'AbortError';
// AbortControllerでリクエストをキャンセルするとAbortErrorがスローされる
}
};
上記の例は、AbortController
が導入される前には不可能だった、ネットワークリクエストのプログラムによるキャンセルを実現しています。キャンセルされると、ブラウザはfetch
を停止し、ネットワーク帯域幅を節約します。重要なのは、このキャンセルが必ずしもユーザーによって開始される必要はないという点です。
controller.signal
はAbortSignal
オブジェクトを提供し、fetch
のような非同期操作との通信を可能にし、それらをキャンセルできるようにします。
複数のシグナルを1つのシグナルに結合する場合は、AbortSignal.any()
を使用できます。以下のように:
try {
const controller = new AbortController();
const timeoutSignal = AbortSignal.timeout(5000);
const response = await fetch(url, {
// いずれかのシグナルがトリガーされた場合にfetchを中止
signal: AbortSignal.any([controller.signal, timeoutSignal]),
});
const data = await response.json();
} catch (error) {
if (error.name === 'AbortError') {
// ユーザーにキャンセルを通知
} else if (error.name === 'TimeoutError') {
// タイムアウトを通知
} else {
// その他のエラー(ネットワーク問題など)を処理
console.error(`種類: ${error.name}, メッセージ: ${error.message}`);
}
}
AbortController
とAbortSignal
の違い
-
AbortController:
controller.abort()
を通じて関連するシグナルを明示的にキャンセルするために使用します。 - AbortSignal: シグナルオブジェクトを表します。それ自体では何もキャンセルできませんが、キャンセル状態を通信します。
AbortSignal
では以下が可能です:
-
signal.aborted
でキャンセルされているかを確認 -
abort
イベントをリッスン:
if (signal.aborted) {
}
signal.addEventListener('abort', () => {});
リクエストがAbortController
でキャンセルされると、サーバーはそれを処理せず応答も送信しないため、帯域幅を節約し、クライアント側のパフォーマンスを向上させます。
AbortControllerの主なユースケース
WebSocket接続のキャンセル
従来のWebSocketのような古いAPIは、AbortSignal
をネイティブにサポートしていません。その代わり、次のようにキャンセルを実装できます:
function abortableSocket(url, signal) {
const socket = new WebSocket(url);
if (signal.aborted) {
socket.close();
// すでにキャンセルされている場合は即時終了
}
signal.addEventListener('abort', () => socket.close());
return socket;
}
注意:AbortSignal
がすでにキャンセルされている場合、abort
イベントはトリガーされません。そのため、このケースを事前に確認して処理する必要があります。
イベントリスナーの削除
従来の方法では、イベントリスナーを削除するには完全に同じ関数参照を渡す必要がありました:
window.addEventListener('resize', () => doSomething());
window.removeEventListener('resize', () => doSomething()); // これは機能しない
AbortController
を使用すると、これが簡単になります:
const controller = new AbortController();
const { signal } = controller;
window.addEventListener('resize', () => doSomething(), { signal });
// abort()を呼び出すことでイベントリスナーを削除
controller.abort();
古いブラウザでは、AbortController
をサポートするためにポリフィルを追加することを検討してください。
Reactフック内での非同期タスクの管理
Reactでは、コンポーネントが更新される前に以前の非同期タスクが完了しない場合、エフェクトが並行して実行されることがあります:
function FooComponent({ something }) {
useEffect(async () => {
const data = await fetch(url + something);
// データを処理
}, [something]);
return <>...</>;
}
このような問題を避けるには、AbortController
を使用して以前のタスクをキャンセルします:
function FooComponent({ something }) {
useEffect(() => {
const controller = new AbortController();
const { signal } = controller;
(async () => {
const data = await fetch(url + something, { signal });
// レスポンスを処理
})();
return () => controller.abort();
}, [something]);
return <>...</>;
}
Node.jsでのAbortControllerの使用
最新のNode.jsでは、AbortController
に対応したsetTimeout
実装が含まれています:
const { setTimeout: setTimeoutPromise } = require('node:timers/promises');
const controller = new AbortController();
const { signal } = controller;
setTimeoutPromise(1000, 'foobar', { signal })
.then(console.log)
.catch((error) => {
if (error.name === 'AbortError') console.log('タイムアウトが中止されました');
});
controller.abort();
ブラウザのsetTimeout
とは異なり、この実装はコールバックを受け入れません。その代わりに.then()
やawait
を使用します。
高度なスケジューリングのためのTaskController
ブラウザはタスクの優先順位付けのためにscheduler.postTask()
を採用し、TaskController
がAbortController
を拡張します。これを使ってタスクをキャンセルしたり、優先順位を動的に調整できます:
const taskController = new TaskController();
scheduler
.postTask(() => console.log('タスクを実行'), { signal: taskController.signal })
.then((result) => console.log(result))
.catch((error) => console.error('エラー:', error));
taskController.abort();
優先順位制御が不要な場合は、単にAbortController
を使用できます。
結論
AbortController
は、現代のJavaScript開発において不可欠なツールであり、非同期タスクを管理およびキャンセルするための標準化された方法を提供します。
そのブラウザおよびNode.js環境への統合は、その汎用性と重要性を示しています。
AbortController
を知らなかった方も、今こそその能力を最大限に活用し、非同期プログラミングツールキットの基盤として採用する時です。
私たちはLeapcell、Node.jsプロジェクトのクラウドデプロイの最適解です。!
Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:
複数言語サポート
- Node.js、Python、Go、Rustで開発できます。
無制限のプロジェクトデプロイ
- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。
比類のないコスト効率
- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。
洗練された開発者体験
- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。
簡単なスケーラビリティと高パフォーマンス
- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。
Xでフォローする:@LeapcellHQ