AbortSignalは、対応するAPIへの中止要求を結び付ける。コントローラーは手動停止、timeout()は時間制限、any()は信号の合成を担う。利用には、受け取る処理の対応範囲と完了・後始末の設計も必要だ。

手動停止と時計の経路が一つの信号ゲートへ集まる図

実行記録ではない概念図。

処理の所有者を決める

中止を要求できるコードがコントローラーを持ち、処理にはsignalを渡す。MDNはこの通信の境界と、fetch、応答本文の消費、ストリームを中止できる範囲を説明する。 コントローラーと対応処理

仮の検索パネルにキャンセルボタンがあるとしよう。現在の検索に属する要求、ボタン処理の所有者、画面で完了が意味する状態を書き出す。別の検索の結果として表示しないための更新条件も決める。信号を渡しただけで画面の方針が完成するわけではない。パネルを閉じた場合に、誰が結果の表示を止めるかも確認する。

理由の型と操作の意図を分ける

reasonはErrorに限らないJavaScriptの値で、中止前はundefinedだ。理由の省略やabort()へのundefinedの指定では既定のAbortErrorになる。プロパティを読む前に値の形を確認する。 理由の値 DOMの既定値規則

DOMが定める中止可能なPromiseベースのウェブプラットフォームAPIには、理由で拒否し、既に中止された信号なら即座に拒否する要件がある。すべてのPromiseに中止機能が付くわけではない。 API統合の要件

検索パネルでボタン操作を区別する必要があれば、その操作を別に記録する設計を考える。例外名だけを利用者の意図の記録にしない。手動停止、制限到達、別の失敗をどんな文言で示すかを決め、捕まえた値を一つの分類へ押し込まない。

入力順と時計の境界を読む

any()に既に中止された入力があれば、反復順で最初の該当入力の理由を使う。なければ最初に中止する入力の理由を受け取り、その後の中止で理由は置き換わらない。合成は他の入力を中止しない。 入力順と理由の伝達

timeout()は活動時間を使う。停止中のワーカーや文書のバックフォワードキャッシュ滞在中には止まり得る。すべての背景タブを指す説明ではない。 活動時間の境界

仮の設計検討では、移動後にパネルへ戻ったとき古い結果が必要かを問う。制限の数字だけでは答えられない。既に中止された入力を組み立てる順序も確認する。「利用者」という名前を付けても、先にある中止入力より自動的に優先されない。レビューには入力の生成箇所と合成箇所を一緒に残すと理解しやすい。

専門家の視点:本文と後始末まで見る

Jake Archibaldによる2017年のChromeの解説は、コントローラーと信号の分離、fetchの中止が応答本文の読み取りにも及ぶ点を示す。当時のブラウザー情報は現在の互換性表ではない。 日付のある専門家解説

MDNの例はresponse.text()の完了までアプリのリスナーを保持し、finallyで除去する。once: trueはイベントが発火した場合にしか除去しないため、成功・失敗時の経路も必要だ。この除去は時間制限の信号やfetch内部のリスナーを管理しない。 アプリのリスナーの寿命 時間制限の信号の寿命

開始、本文処理、終了の点検表として使う。画面更新を止める担当、リスナー除去の位置、対象ブラウザーの各メソッド対応を実環境で確認する必要がある。2026-10-12、担当者がmacOS上のChromeでlocalhostからこの独立したローカルwait()デモを実行した。3言語すべてでボタンのUTF-8表示を確認し、手動中止ではAbortError: signal is aborted without reason、操作しないタイムアウトではTimeoutError: signal timed outが表示された。この確認はfetch/通信の中止、BFCache、他のブラウザーを対象としない。

検索パネルの検討では、担当者が「どの検索の結果か」「閉じた画面へ表示してよいか」「どの終了経路で後始末するか」の三問に答えられるようにする。例えば新しい検索を始めた直後に以前の結果が届いた場合を、紙上で追ってみる。これは仮想のレビュー手順であり、信号だけが表示順を保証するという主張ではない。実装と試験で確認すべき条件を明示するための例だ。

実行できる最小のDOM例

次のHTMLをUTF-8で保存してブラウザーで開くと、ボタンによる中止と3秒の時間制限を一つの信号にまとめる動作を確認できる。wait()は信号に従うように書いたデモ用の処理だ。実際のAPIではfetch(url, { signal })のように信号を渡す。

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>AbortSignalの中止デモ</title>
</head>
<body>
<button id="cancel">中止</button>
<pre id="result">8秒の処理を待っています</pre>
<script>
const result = document.querySelector("#result");
const controller = new AbortController();
const signal = AbortSignal.any([
  controller.signal,
  AbortSignal.timeout(3000),
]);
document.querySelector("#cancel").addEventListener("click", () => controller.abort());

function wait(ms, signal) {
  return new Promise((resolve, reject) => {
    if (signal.aborted) return reject(signal.reason);
    const timer = setTimeout(() => { cleanup(); resolve("完了"); }, ms);
    const onAbort = () => { clearTimeout(timer); cleanup(); reject(signal.reason); };
    const cleanup = () => signal.removeEventListener("abort", onAbort);
    signal.addEventListener("abort", onAbort, { once: true });
  });
}

wait(8000, signal)
  .then(message => { result.textContent = message; })
  .catch(error => { result.textContent = `${error.name}: ${error.message}`; });
</script>
</body>
</html>

予想される結果: 3秒以内にボタンを押すと既定の中止理由AbortErrorが表示され、押さなければ約3秒後にTimeoutErrorが表示される。8秒の処理なので、通常完了より先にどちらかの中止経路に入る。このローカルデモは信号とイベント処理を示し、ネットワーク要求は送らない。対象ブラウザーがAbortSignal.any()とtimeout()に対応するか確認する。

出典