私が歌川です

@utgwkk が書いている

fetch APIにおけるHTTPリクエストの中断・タイムアウト

AbortControllerを使うことで実現できる。

MDNにも書いてあるけど、以下の操作でfetch APIによるHTTPリクエストを中断できる。

  1. fetch() の第2引数のオブジェクトの signal フィールドに AbortController.signal を渡す
  2. AbortController.abort() を呼ぶ

HTTPリクエストが中断されると、 fetch() が返すPromiseはrejectされる。

ユーザー操作でリクエストを中断する

MDNのサンプルコードデモを参照。

タイムアウトさせる

window.setTimeout() のコールバック関数内で AbortController.abort() を呼ぶとできる。 タイムアウトを過ぎなかったときのために window.clearTimeout() しているけど、いらないかもしれない。

const invokeAPI = async (url) => {
  const controller = new AbortController();
  const timer = window.setTimeout(() => {
    controller.abort();
  }, 1000);
  const response = await fetch(url, {
    signal: controller.signal,
  });
  window.clearTimeout(timer);
  return resp;
};

Promise.race() でタイムアウトさせる場合との違い

Promise.race()fetch() とタイムアウトさせるPromiseを並走させてもタイムアウトは実現できそうに見える。

const timeout = (msec) => {
  return new Promise((_, reject) => {
    window.setTimeout(() => reject('timeout'), msec);
  });
};

await Promise.race([fetch(...), timeout(1000)]);

たしかにタイムアウトできているように見えるけど、AbortControllerを使った場合と違ってHTTPリクエスト自体はキャンセルされない。

一方axiosでは

cancel tokenという仕組みでHTTPリクエストをキャンセルできる。また、リクエストに対してタイムアウトを設定できる

cancel tokenはcancelable PromiseというECMAScriptのproposalに基づいて実装されたらしいけど、このproposalは取り下げられた。Promiseをキャンセルしたいという欲求がありそう。