この記事の目的
- Promise と Observable の違いを 業務レベルの具体例 で理解する
- UI フレームワーク(特に Angular)で Observable が選ばれる理由を知る
- 「いつ Promise を使い、いつ Observable を使うべきか」を判断できるようになる
想定読者
- TypeScript / Angular の実務経験 1〜3 年目
- RxJS を“なんとなく”使っているが、本質を掴みたい人
- Angular の HttpClient が Promise を採用しない理由を知りたい人
1. Promise と Observable の違い(業務で発生する問題から理解する)
■ Promise が「バグの原因になる」典型パターン
ケース1:サジェスト検索(入力のたびに API 呼び出し)
Promise で実装すると…
- 高速タイピングで 前の API をキャンセルできない
- 古いレスポンスが後から返って UI が書き換わる
- → 実務で最も多い UI バグ
Observable なら…
-
debounceTime:入力が落ち着くまで待つ -
distinctUntilChanged:同じ内容は無視 -
switchMap:古いリクエストを自動キャンセル
ケース2:5秒ごとの API ポーリング(在庫確認・セッション延命)
Promise:
- setInterval と then が混じり可読性が低い
- 中断が難しい
Observable:
interval(5000).pipe(
switchMap(() => this.http.get('/api/status'))
).subscribe(status => updateUI(status));
ケース3:画面遷移後に非同期処理が返り UI が壊れる
Promise:
- キャンセル不可
- 画面破棄後に値が返り UI が壊れる
- メモリリークの原因にもなる
Observable:
- unsubscribe により確実に停止
2. Angular の HttpClient が Observable の理由(深掘り)
★理由1:HTTP は「単発」ではなく「UI 状態の一部」だから
SPA では以下が日常的に起きる:
- 入力値の変化
- 再リクエスト
- 画面遷移
- リアルタイム更新
Promise は「単発前提」のため UI 変化に追従できない。
Observable は 継続する値の流れ(ストリーム) なので UI と相性が良い。
★理由2:HTTP キャンセルが必須(Promise では不可能)
UI では高速操作が当たり前。
- ユーザーが検索ワードを連続入力
- 画面移動中に API が返る
- 古い結果が UI に反映される危険
Observable → unsubscribe() ができる
Promise → ❌ できない
★理由3:switchMap による「古いリクエストの自動キャンセル」
this.keyword$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(keyword => this.http.get(`/api/search?q=${keyword}`))
).subscribe(console.log);
★理由4:非同期処理と UI 状態の合成を容易にするため
Promise:
- then チェーン地獄
- 複雑な合成が難しい
Observable:
combineLatest-
map,switchMap,catchError
★理由5:メモリリーク防止(unsubscribe)が重要
Promise:
- 停止できず処理が残る → メモリリークの温床
Observable:
- 破棄時に unsubscribe → 安全
3. 結論:Angular が Observable を採用した理由まとめ
| Angular が必要とする要件 | Promise | Observable |
|---|---|---|
| UI と同期した API 呼び出し | ✕ | ◎ |
| キャンセル | ✕ | ◎ |
| 古いリクエストの破棄 | ✕ | ◎ |
| ポーリング | △ | ◎ |
| 複数ストリームの合成 | △ | ◎ |
| メモリリーク防止 | ✕ | ◎ |
4. サンプルコード:サジェスト検索
@UntilDestroy()
@Component({
selector: 'app-search',
template: `<input (input)="onInput($event.target.value)" />`
})
export class SearchComponent {
private keyword$ = new Subject<string>();
results$ = this.keyword$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(keyword =>
this.http.get(`/api/search?q=${keyword}`)
)
);
constructor(private http: HttpClient) {}
onInput(value: string) {
this.keyword$.next(value);
}
}
5. 例え話:Promise vs Observable
Promise = 単発の宅配便
- 送れば届く
- キャンセル不可
- 住所(UI)が変わっても持ってこられる(バグの原因)
Observable = 配送システム
- 状態が変われば配送先も変わる
- 配送停止(unsubscribe)が可能
- イベント・入力・ポーリングなどの連続値に対応
6. まとめ
- Promise は「単発処理」向け
- Observable は「UI × 非同期」向け
- Angular の HttpClient が Observable を採用する理由は
UI の安全性・柔軟性・キャンセル制御 を満たすため