コンテンツにスキップ

JavaScript/AbortSignal

出典: フリー教科書『ウィキブックス(Wikibooks)』

AbortSignal インターフェースは、ウェブ API での操作の中止(アボート)を制御するためのシグナル機構を提供します。これは主に fetch() リクエストや非同期操作などの長時間実行される処理をキャンセルするために使用されます[1]

インターフェース

[編集]
WebIDLによる定義
[Exposed=*]
interface AbortSignal : EventTarget {
  [NewObject] static AbortSignal abort(optional any reason);
  [Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
  [NewObject] static AbortSignal _any(sequence<AbortSignal> signals);

  readonly attribute boolean aborted;
  readonly attribute any reason;
  undefined throwIfAborted();

  attribute EventHandler onabort;
};

静的メソッド

[編集]

AbortSignal.abort()

[編集]
AbortSignal.abort(optional any reason)

すでにアボート済みの新しい AbortSignal オブジェクトを作成します。

  • reason (オプション): アボートの理由を示す値。指定されない場合、デフォルトで "AbortError" DOMException となります。

AbortSignal.timeout()

[編集]
AbortSignal.timeout(unsigned long long milliseconds)

指定されたミリ秒後に自動的にアボートする新しい AbortSignal オブジェクトを作成します。

  • milliseconds: タイムアウトするまでのミリ秒数。

AbortSignal._any()

[編集]
AbortSignal._any(sequence<AbortSignal> signals)

指定された AbortSignal オブジェクトのシーケンスのいずれかがアボートされたときにアボートする新しい AbortSignal オブジェクトを作成します。

プロパティ

[編集]

aborted

[編集]

アボートされたかどうかを示す真偽値。アボートされている場合は true、そうでない場合は false を返します。

reason

[編集]

アボートの理由を示す値。アボートされていない場合は undefined を返します。

onabort

[編集]

アボート時に実行されるイベントハンドラー。

メソッド

[編集]

throwIfAborted()

[編集]
throwIfAborted()

シグナルがアボートされている場合、reason をスローします。アボートされていない場合は何もしません。

[編集]

AbortController と fetch() の使用例

[編集]

以下のプログラムは、AbortSignalAbortController を使用して fetch() リクエストをキャンセルする方法を示しています。

// AbortController のインスタンスを作成
const controller = new AbortController();
const signal = controller.signal;

// タイムアウトを設定(5秒後にキャンセル)
setTimeout(() => {
  controller.abort(); // リクエストをキャンセル
  console.log('リクエストがタイムアウトによりキャンセルされました');
}, 5000);

try {
  // fetch リクエストに signal を渡す
  const response = await fetch('https://example.com/large-data', { signal });
  const data = await response.json();
  console.log('データを受信しました:', data);
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('リクエストがキャンセルされました');
  } else {
    console.error('エラーが発生しました:', error);
  }
}

このプログラムでは、AbortController を作成し、その signal プロパティを fetch() リクエストに渡しています。5秒後にコントローラーの abort() メソッドが呼び出され、リクエストがキャンセルされます。

AbortSignal.timeout() の使用例

[編集]

以下のプログラムは、AbortSignal.timeout() を使用して一定時間後に自動的にアボートするシグナルを作成する方法を示しています。

// 3秒後にタイムアウトする AbortSignal を作成
const timeoutSignal = AbortSignal.timeout(3000);

try {
  // fetch リクエストにタイムアウトシグナルを渡す
  const response = await fetch('https://example.com/api/data', { signal: timeoutSignal });
  const data = await response.json();
  console.log('データを受信しました:', data);
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('リクエストが3秒のタイムアウトによりキャンセルされました');
  } else {
    console.error('エラーが発生しました:', error);
  }
}

このプログラムでは、AbortSignal.timeout() を使用して3秒後に自動的にアボートするシグナルを作成し、fetch() リクエストに渡しています。

AbortSignal._any() の使用例

[編集]

以下のプログラムは、AbortSignal._any() を使用して複数のシグナルのいずれかがアボートされたときにアボートするシグナルを作成する方法を示しています。

// タイムアウト用の AbortSignal を作成(10秒)
const timeoutSignal = AbortSignal.timeout(10000);

// ユーザーアクション用の AbortController を作成
const userController = new AbortController();

// 「キャンセル」ボタンのクリックイベントをリッスン
document.getElementById('cancelButton').addEventListener('click', () => {
  userController.abort('ユーザーによるキャンセル');
});

// 複数のシグナルのいずれかがアボートされたときにアボートする AbortSignal を作成
const combinedSignal = AbortSignal._any([timeoutSignal, userController.signal]);

// アボートイベントをリッスン
combinedSignal.addEventListener('abort', () => {
  console.log('操作がキャンセルされました。理由:', combinedSignal.reason);
});

try {
  // fetch リクエストに結合されたシグナルを渡す
  const response = await fetch('https://example.com/large-file', { signal: combinedSignal });
  const blob = await response.blob();
  console.log('ファイルをダウンロードしました:', blob.size, 'バイト');
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('リクエストがキャンセルされました');
  } else {
    console.error('エラーが発生しました:', error);
  }
}

このプログラムでは、AbortSignal._any() を使用して、タイムアウトまたはユーザーアクションのいずれかによってアボートされるシグナルを作成しています。このシグナルは、fetch() リクエストに渡され、10秒のタイムアウトまたはユーザーが「キャンセル」ボタンをクリックするとアボートされます。

throwIfAborted() の使用例

[編集]

以下のプログラムは、throwIfAborted() メソッドを使用してカスタム非同期操作内でアボートチェックを行う方法を示しています。

// カスタム非同期操作を実装
async function processLargeData(data, signal) {
  const results = [];
  
  // データの各チャンクを処理
  for (const chunk of data) {
    // 各処理の前にアボートチェック
    signal.throwIfAborted();
    
    // 時間のかかる処理をシミュレート
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // データを処理
    results.push(chunk * 2);
  }
  
  return results;
}

// 使用例
async function main() {
  const controller = new AbortController();
  const data = Array.from({ length: 100 }, (_, i) => i);
  
  // 3秒後にキャンセル
  setTimeout(() => controller.abort('処理時間が長すぎます'), 3000);
  
  try {
    const results = await processLargeData(data, controller.signal);
    console.log('処理が完了しました:', results);
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('処理がキャンセルされました。理由:', error);
    } else {
      console.error('エラーが発生しました:', error);
    }
  }
}

main();

このプログラムでは、カスタム非同期操作内で signal.throwIfAborted() を使用して各処理ステップの前にアボートチェックを行っています。処理が長すぎる場合、コントローラーの abort() メソッドが呼び出され、処理がキャンセルされます。

注意点

[編集]
  • 互換性: AbortSignal.timeout()AbortSignal._any() は比較的新しいメソッドであり、すべてのブラウザでサポートされているわけではありません。
  • 複数操作の中止: 同じ AbortSignal を複数の操作に渡すことで、それらをまとめてキャンセルすることができます。
  • リソース解放: アボートされた操作は関連するリソースを適切に解放する必要があります。
  • アボート後の処理: アボート後に操作の結果を使用しようとすると、エラーが発生する可能性があります。

脚註

[編集]
  1. ^ DOM 標準によって定義されており、ブラウザや Node.js などの JavaScript 実行環境でサポートされています。

外部リンク

[編集]