Dev Study
← 解説「Observable と RxJS — 時間とともに届く値」に戻る

サンプルコードで身につける: Observable と RxJS — 時間とともに届く値

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1of と subscribe — 最小の Observable

of(1, 2, 3) は「1, 2, 3 を順に流す Observable」を作ります。Observable は作っただけでは何も起きず、subscribe(購読)して初めて値が流れ、コールバックが1つずつ受け取ります。「値の流れを定義してから、購読して受け取る」という基本の型をこの最小例で押さえましょう。

import { of } from 'rxjs';

// 「1, 2, 3 を順に流す」という値の流れを定義する
const numbers$ = of(1, 2, 3);

// subscribe して初めて値が流れ始める
numbers$.subscribe((value) => {
  console.log('届いた値:', value);
});

// 出力:
// 届いた値: 1
// 届いた値: 2
// 届いた値: 3

// 変数名の末尾に $ を付けるのは「Observableである」印の慣習

2interval — 時間とともに届く値

interval(1000) は1秒ごとに 0, 1, 2, ... と値を流し続ける Observable です。1回で終わる Promise と違い、Observable は時間をまたいで何度も値を届けられるのが特徴です。「あとから何度も届く値の流れ」という Observable のイメージをつかむのに最適な例です。

import { interval } from 'rxjs';

// 1000ミリ秒ごとに 0, 1, 2, ... を流し続ける Observable
const tick$ = interval(1000);

tick$.subscribe((count) => {
  console.log(count + ' 回目の通知');
});

// 出力(1秒ごとに1行ずつ増えていく):
// 0 回目の通知
// 1 回目の通知
// 2 回目の通知
// ...止めるまで永遠に続く

3map で流れる値を加工する

pipe() の中に map を書くと、流れてくる値を1つずつ変換した新しい流れが作れます。元の Observable はそのままに、「加工後の流れ」を派生させるのが RxJS の考え方です。APIのレスポンスを表示用の形に整えるなど、実務では subscribe の前に pipe で加工するのが定番の形です。

import { of, map } from 'rxjs';

const prices$ = of(100, 250, 980);

// pipe + map で「税込価格の流れ」に変換する
const taxed$ = prices$.pipe(
  map((price) => Math.floor(price * 1.1)),
);

taxed$.subscribe((price) => {
  console.log('税込:', price, '円');
});

// 出力:
// 税込: 110 円
// 税込: 275 円
// 税込: 1078 円

4HttpClient が返す Observable

HttpClient の get() は結果を Observable で返すため、subscribe して初めてリクエストが飛び、レスポンスが届くとコールバックが呼ばれます。of や interval で見た「subscribe して値を受け取る」型が、通信でもそのまま通用することを示す例です。1回きりの通信結果も「値の流れ」として同じ仕組みに乗っています。

import { Component, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';

type User = { id: number; name: string };

@Component({
  selector: 'app-user-list',
  template: '@for (u of users(); track u.id) { <p>{{ u.name }}</p> }',
})
export class UserListComponent {
  private http = inject(HttpClient);
  users = signal<User[]>([]);

  load() {
    // get() は Observable を返す。subscribe して初めて通信が始まる
    this.http.get<User[]>('/api/users').subscribe((users) => {
      this.users.set(users); // レスポンスが届いたら状態に反映
    });
  }
}

5unsubscribe — 購読の後片付け

interval のように終わらない Observable を subscribe すると、止めない限り処理が動き続けます。subscribe の戻り値(Subscription)を取っておき、不要になったら unsubscribe() で止めるのが基本の後片付けです。コンポーネントが消えても購読が残るとメモリリークになるため、実務では解除とセットで考える習慣をつけます。

import { interval, Subscription } from 'rxjs';

// 終わらない流れは、止める手段とセットで持つ
const sub: Subscription = interval(1000).subscribe((count) => {
  console.log('経過:', count);
});

// 5秒後に購読を解除すると、それ以降コールバックは呼ばれない
setTimeout(() => {
  sub.unsubscribe();
  console.log('購読を解除しました');
}, 5000);

// 解除を忘れると処理が動き続け、メモリリークの原因になる。
// コンポーネントでは ngOnDestroy で解除するのが定番(次のレッスンで学ぶ)