Promise.race でタイムアウトを作っても、競わせた処理は自動では止まりません。待つ側がエラーを受け取ったあとに、本来の処理の完了ログが出る例を実測しました。
Claude Code のコードでも、待機と中断を分けて読みます。前回は結果の選び方、今回は処理の継続です。
2026年11月9日、macOS 27.2・Node.js v24.22.1でタイマーを実測しました。ブラウザ・HTTP通信・サーバー側の中断は未検証です。
タイムアウト後にも完了する
race.mjs に保存し node race.mjs で実行します。タイマーの数値はミリ秒の指定値で、実行時刻の保証ではありません。
import { setTimeout as sleep } from 'node:timers/promises';
const work = sleep(60).then(() => console.log('work: completed'));
try {
await Promise.race([
work,
sleep(10).then(() => { throw new Error('timeout'); }),
]);
} catch (error) {
console.log('race:', error.message);
}
await work;
出力です。
race: timeout
work: completed
catch のあとにも完了しました。最後の await work は観測用です。
race は最初の決着を採用し、入力を止めません。今回の処理は sleep(60) の呼び出しで開始します。
中断に対応する処理へsignalを渡す
node:timers/promises の setTimeout は AbortSignal を受け取れます。別の abort.mjs として実行します。
import { setTimeout as sleep } from 'node:timers/promises';
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10);
try {
await sleep(60, undefined, { signal: controller.signal });
console.log('abortable work: completed');
} catch (error) {
console.log('abortable work:', error.name);
} finally {
clearTimeout(timeout);
}
await sleep(80);
console.log('check: end');
出力です。
abortable work: AbortError
check: end
中断に対応する sleep へsignalを渡すと、成功時の完了ログは出ませんでした。末尾の待機は後からログが出ないか見る実験用です。
finally では通知タイマーを片付けます。処理が先に終わった場合にも、あとから中断通知が発火するのを避けます。
中断への対応が必要
処理する側がsignalを受け取り、中断に対応する必要があります。
signalを渡さず、別のcontrollerで abort() を呼んでも結び付きません。
HTTP通信でサーバー側の変更も取り消せるかは未検証です。変更済みの可能性があり、再送の可否は別に判断します。
生成されたコードでは、次を確認します。
-
Promise.raceのcatch以降にも、本来の処理が完了する経路があるか。 - 中断したい処理がsignalを受け取れるか。実際に引数として渡しているか。
- 成功・失敗・中断のあとに、不要になったタイマーを片付けているか。
参照
未経験者向けの体験版を公開しています
- ▶ ライブデモ(インストール不要・無料)→ https://trial-web-ayies128s-projects.vercel.app/?ref=qiita
- 無料体験版(git clone して手元で動かす・⭐ Star もよろしくお願いします)→ https://github.com/ayies128/next-ai-camp-trial
- YouTube『AIエンジニア情報局』(AI×開発ニュースを1本5分で)→ https://www.youtube.com/channel/UC1rXVD9WYsQPQEWZyd-A1KA/?ref=qiita
※ 中上級者には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。