Vanilla JavaScriptで学ぶRxJS基礎コース
1. リアクティブプログラミングとは
マウスのクリック、テキスト入力、HTTPレスポンス。これらは全部「時間とともに流れてくるデータ」です。リアクティブプログラミングは、そのデータの流れ(ストリーム)を扱う考え方です。
「データ来たかな?」と何度も聞きに行くのではなく、届いた瞬間に反応する。これだけの発想の転換なんですが、非同期処理の見え方が劇的に変わります。
2. RxJSとは
RxJSは、Observableを中心に非同期処理やイベント処理を組み立てるJavaScriptライブラリです。ざっくり言えばPromiseの強化版で、複数回値を流せるし、途中でキャンセルもできる。
インストール
npm:
npm install rxjsCDN:
<script src="https://unpkg.com/rxjs@7/dist/bundles/rxjs.umd.min.js"></script>Vanilla JSの基本構成
index.html、app.js、あとはお好みでstyles.css。この3ファイルだけで始められます。フレームワークは不要です。
3. Observable
Observableは、時間の経過とともに0件、1件、複数件の値を発行するデータソースです。私は「値を流す蛇口」みたいに理解しています。ひねると値が出て、閉めると止まる。
const { of, from, interval } = rxjs;
of(1, 2, 3).subscribe(console.log); // 1, 2, 3
from(["A", "B"]).subscribe(console.log); // A, BfromEvent
DOMイベントもObservableに変換できます。個人的に、RxJSの便利さが一番実感できる機能だと思っています。
const { fromEvent } = rxjs;
const button = document.getElementById("btn");
const click$ = fromEvent(button, "click");
const sub = click$.subscribe(() => console.log("click"));subscribe / unsubscribe
購読を始めると値が流れてきますが、使い終わったら忘れずに解除してください。intervalやfromEventのような無限ストリームは放置するとメモリリークの原因になります。
const { interval } = rxjs;
const sub = interval(1000).subscribe(v => console.log("tick", v));
setTimeout(() => sub.unsubscribe(), 5000);4. 基本オペレーター
オペレーターはObservableの値を変換したりフィルタしたりする関数です。pipeで繋げて使います。この「繋ぐ」感覚がRxJSの核心で、UNIXのパイプと似た感覚で扱えます。
const { of } = rxjs;
const { map, filter, take } = rxjs.operators;
of(1, 2, 3, 4, 5)
.pipe(
filter(n => n % 2 === 1),
map(n => n * 10),
take(2)
)
.subscribe(console.log); // 10, 30このコースでよく使うオペレーターを挙げておきます。生成系はof、from、interval、timer。変換系はmap、filter。結合系はmerge、concat。抽出系はtake、first、debounceTime。
5. DOMイベントの処理
クリックカウンター
一番シンプルな例です。クリックをカウントして画面に表示します。
const { fromEvent } = rxjs;
const btn = document.getElementById("btn");
const output = document.getElementById("output");
let count = 0;
fromEvent(btn, "click").subscribe(() => {
count += 1;
output.textContent = `Clicks: ${count}`;
});ライブ検索(基本)
入力欄の値を監視して、300ms入力が止まったら「検索」とみなす。このdebounceTimeのパターンは実務でも本当によく使います。
const { fromEvent } = rxjs;
const { map, debounceTime } = rxjs.operators;
const input = document.getElementById("search");
fromEvent(input, "input")
.pipe(
map(e => e.target.value.trim()),
debounceTime(300)
)
.subscribe(term => console.log("Search:", term));6. 非同期処理
Observable と Promise の違い
Promiseは通常1回だけ値を返します。一方Observableは、複数回値を流せてunsubscribeで中断できる。この差が、イベント処理やポーリングでは決定的に効いてきます。
RxJSのajaxでAPI呼び出し
const { ajax } = rxjs.ajax;
ajax
.getJSON("https://jsonplaceholder.typicode.com/users")
.subscribe({
next: data => console.log("Users:", data),
error: err => console.error("HTTP error:", err),
complete: () => console.log("Completed")
});7. エラーハンドリングと完了処理
const { of } = rxjs;
const { catchError, retry } = rxjs.operators;
const { ajax } = rxjs.ajax;
ajax
.getJSON("https://api.example.com/data")
.pipe(
retry(2),
catchError(err => {
console.error("Request failed:", err);
return of([]);
})
)
.subscribe({
next: data => console.log(data),
complete: () => console.log("Finished")
});覚えておきたいのは4つだけです。catchErrorはエラー時の代替処理、retryは失敗時の再試行、completeは正常終了時の処理、unsubscribeは手動での停止。これだけ押さえておけば、実務の大部分はカバーできます。
8. ベストプラクティス
実際に使っていて気をつけていることです。
無限ストリーム(interval、fromEvent)は必ず購読解除する。pipeでオペレーターを連結して、読みやすさを保つ。重い処理をsubscribeのコールバックに詰め込まない。デバッグにはtapを挟むと、値の流れが追いやすくなります。
9. 最終プロジェクト(提案)
ここまでの内容を1つのアプリにまとめてみましょう。ライブ検索ミニアプリを作ります。
テキスト入力を作り、fromEventで入力イベントを監視、mapとdebounceTimeで整形、ajax.getJSONでAPIを呼んで、結果を画面に表示。最後にcatchErrorでエラー処理を入れる。これだけでも基礎内容が1本の実践フローで繋がります。
10. 参考リンクと次のステップ
公式ドキュメントで読んでおきたいページ:
練習課題をいくつか。intervalでストップウォッチ、+/-ボタンのカウンター、公開APIを使ったライブ検索、retryによる再試行処理。どれも30分もあれば作れます。
次は中級編でswitchMap、mergeMap、Subject、ストリーム合成に進むと、実務で使える幅がぐっと広がります。