非同期UIの一時URLは完了状態ではなく所有権移譲時に台帳へ登録する
Blob の一時 URL を非同期 OCR、アップロード、キュー処理などへ引き渡す UI では、完了後の画面 state だけを cleanup 元にすると、処理待ちのまま unmount した資源や、終了後に遅れて届いた追加資源が台帳に載らずリークする。
採用するのは、URL の所有権を受け取った瞬間に単一の Set へ登録する方式である。最初の入力は非同期処理を始める前に登録し、遅延して到着した追加入力は stale 判定より先に登録する。解放関数は Set から削除できた URL だけを revoke する。これにより clear、失敗、stale completion、unmount のどこから同じ解放関数を呼んでも、二重解放を避けながら未解放分だけ処理できる。
state 配列を結合して分岐ごとに revoke する方式は、pending 中の所有物が state に未反映だと漏れ、分岐が増えるほど解放対象の対称性を保ちにくい。そのため表示状態と資源所有権を分離し、資源台帳を cleanup の唯一の根拠にする。
teardown では先に世代番号やセッション ID を進めて非同期完了を無効化し、その後に台帳を全解放する。遅延結果は現在の世代と一致しない限り state を更新せず、先に台帳へ登録済みなので直ちに同じ解放経路へ流せる。
検証
制御可能な遅延 Promise を使い、非同期処理中に unmount する。表裏など複数 URL がそれぞれちょうど一度 revoke されることを確認し、その後 Promise を完了させても revoke 回数が増えず、state 更新も起きないことを確認する。通常完了だけでなく、追加入力待ちと遅延完了を含むライフサイクル境界をテスト対象にする。