2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

キャンセル・エラー・リトライ:制御構造としてのストリーム設計

Posted at

概要

非同期処理において最も難しいのは、
**「エラー」「キャンセル」「リトライ」**という“制御不能な例外的状態”の取り扱いである。

Rxではこれらを**「設計されたストリーム制御の一部」**として構築できる。
本稿では、非同期におけるトラブル制御をRxの機能でどのように安全かつ柔軟に実現するかを、設計レベルで解説する。


1. なぜキャンセル・エラー処理は複雑化するのか?

従来の非同期処理(Promise, callback)では以下の問題が頻発する:

  • リクエスト競合による無駄なIO
  • 古い結果によるUIの汚染
  • ネットワーク失敗時の再試行設計の煩雑さ
  • 例外処理のネスト地獄

2. Rxにおけるキャンセル:unsubscribe()

const obs = interval(1000)
const sub = obs.subscribe(console.log)

setTimeout(() => sub.unsubscribe(), 3000)
  • unsubscribe() により ストリームの発行元からの切断が可能
  • UIやユーザー操作など、途中で止めたい処理に最適

3. switchMapによる自動キャンセル

search$
  .pipe(
    debounceTime(300),
    switchMap(query => fetchResults(query)) // 前のリクエストは自動キャンセル
  )
  .subscribe(render)
  • ユーザー入力に対するAPIリクエストなど、最新以外を無視したい場合に効果的
  • switchMap は「構造としてのキャンセル

4. エラーハンドリング:catchError

ajax.getJSON('/api/data')
  .pipe(
    catchError(err => {
      console.error(err)
      return of([]) // エラー時は空配列を返す
    })
  )
  .subscribe(handleResult)
  • try/catch ではなく、ストリーム内でエラーも合成
  • 「失敗したらこうする」も構造化できる

5. リトライ:retry, retryWhen

ajax.getJSON('/api/data')
  .pipe(retry(3)) // 最大3回まで自動リトライ
  .subscribe(handleResult)

ajax.getJSON('/api/data')
  .pipe(
    retryWhen(errors =>
      errors.pipe(
        tap(() => console.log("Retrying...")),
        delay(2000)
      )
    )
  )
  .subscribe(handleResult)
  • retryWhen により リトライタイミング・回数・間隔を完全制御可能
  • ネットワークエラー・一時的なAPI障害の対応に有効

6. finalize:完了/キャンセル後の後処理

fetchData$
  .pipe(
    tap(() => isLoading$.next(true)),
    finalize(() => isLoading$.next(false))
  )
  .subscribe(handleData)
  • 成功でもエラーでも、最終処理を共通で定義
  • finally のRxバージョン

実務でのパターンまとめ

パターン 演算子 説明
キャンセル unsubscribe() / switchMap 明示的または自動で処理を中断する
エラーハンドリング catchError() エラーを補足してストリームを継続させる
リトライ retry(n) / retryWhen() 回数やタイミングを制御した再実行
共通後処理 finalize() 成功・失敗を問わず後処理を一括で行う

よくある誤解と対策

❌ ストリームでキャンセルやエラー処理までやるのは複雑

→ ✅ No。複雑な非同期を、構造として記述できるからこそ、保守性が上がる


❌ retryは自動で何度も走って危険

→ ✅ 制御できる。retryWhen回数・条件・間隔を設計に落とし込める


❌ エラー処理がストリームに混ざると読みにくい

→ ✅ catchError副作用を外に出す構造。設計次第で読みやすくなる(関数化・分離など)


結語

Rxは、エラーやキャンセル、リトライといった“例外的制御”を、設計として組み込むことができる数少ないパラダイムである。

  • switchMapによる構造的キャンセル
  • catchErrorでの明示的な失敗処理
  • retryWhenによる戦略的な再試行
  • finalizeによる収束

リアクティブな制御構造とは、
“混沌とした非同期の分岐や失敗を、秩序ある構文と流れに還元するための設計哲学である。”

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?