概要
非同期処理において最も難しいのは、
**「エラー」「キャンセル」「リトライ」**という“制御不能な例外的状態”の取り扱いである。
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による収束
リアクティブな制御構造とは、
“混沌とした非同期の分岐や失敗を、秩序ある構文と流れに還元するための設計哲学である。”