【要約】`Promise.race()` のタイムアウトと裏で動き続ける Promise [Qiita_Trend] | Summary by TechDistill
> Source: Qiita_Trend
Execute Primary Source
// Problem
開発者が非同期処理にタイムアウトを実装する際、
Promise.race() の挙動を誤解することでリソースリークを招く問題がある。- ・
Promise.race()は「結果の採用」を止めるだけで、実行中のPromiseを停止させる機能を持たない。 - ・タイムアウト後も、ネットワーク通信やDNS lookupの処理が裏で動き続ける。
- ・大量の並列処理を行うと、ソケットやファイルディスクリプタが枯渇し、プロセス全体の接続能力を低下させる。
// Approach
開発者がリソース消費を抑えるため、タイムアウトを「待ち時間の打ち切り」ではなく「処理の中断」として扱う手法を採用する。
- ・
AbortControllerを用い、fetch等の対応APIへ中断信号を伝播させる。 - ・
dns/promisesでは、Resolverインスタンスを作成しresolver.cancel()を実行する。 - ・
AbortSignalを引数に取るwithDeadlineのような、汎用的なラッパー関数を実装し、呼び出し側で中断を制御する。
// Result
適切なキャンセル処理を導入することで、高負荷な環境下でもリソースを効率的に管理できる。
- ・
fetchやsetTimeoutにおいて、タイムアウト時に即座に処理を中断できる。 - ・DNSクエリを確実に打ち切ることで、名前解決リソースの圧迫を防げる。
- ・タイムアウト、キャンセル、並列数制限を組み合わせ、堅牢なI/O制御を実現できる。これにより、大規模なトラフィック下でもシステムの安定性を維持できる。
Senior Engineer Insight
> 「待ち」と「実行」の分離は、大規模システムにおいて致命的な設計ミスになり得る。
Promise.race() は、メモリ内処理などの低コストなケースに限定すべきだ。I/Oが絡む場合は、AbortSignal による中断伝播を設計の標準に組み込む必要がある。単なるタイムアウトだけでなく、並列数制限やレート制限との組み合わせが、実戦における防御策として不可欠である。