この記事ではRxJSの『withLatestFromオペレータ』について、以下の内容を図とサンプルコードを用いて分かりやすく解説します。
withLatestFrom
オペレータとはwithLatestFrom
オペレータの「使い方」withLatestFrom
オペレータの「特徴」withLatestFrom
オペレータとcombineLatest
オペレータの違いと使い分け
RxJSのwithLatestFromとは
RxJSのwithLatestFromは、メインのObservableの値が発行された際に、サブのObservableから最新の値を取得して組み合わせて、新しいObservableを出力するオペレータです。
以下にwithLatestFrom
を用いたサンプルコードを示しています。
import { timer } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
// 1000ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const mainSource$ = timer(1000, 1000);
// 1500ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const subSource$ = timer(1500, 1000);
// withLatestFromを使用して、2つのObservableの最新の値を組み合わせて新しいObservableを出力する
const combined$ = mainSource$.pipe(withLatestFrom(subSource$));
combined$.subscribe((value) => console.log(value));
// ログ
// [ 1, 0 ]
// [ 2, 1 ]
// [ 3, 2 ]
// [ 4, 3 ]
// [ 5, 4 ]
// [ 6, 5 ]
上記のサンプルコードでは、withLatestFrom
を使用して、mainSource$
(メインのObservable)が値を発行するたびに、subSource$
(サブのObservable)から最新の値を取得し、結果を出力しています。
あわせて読みたい
『timerオペレータ』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 続きを見る【RxJS】timerオペレータとは?「使い方」や「プログラム例」などを解説!
withLatestFrom
の返り値は配列のObservableなので、以下のプログラムに示すように、配列の分割代入を使用することもできます。
import { timer } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
// 1000ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const mainSource$ = timer(1000, 1000);
// 1500ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const subSource$ = timer(1500, 1000);
// withLatestFromを使用して、2つのObservableの最新の値を組み合わせて新しいObservableを出力する
const combined$ = mainSource$.pipe(withLatestFrom(subSource$));
// 配列の分割代入を使用
combined$.subscribe(([mainSource, subSource]) => {
console.log(`mainSource:${mainSource} subSource:${subSource}`);
});
// ログ
// mainSource:1 subSource:0
// mainSource:2 subSource:1
// mainSource:3 subSource:2
// mainSource:4 subSource:3
// mainSource:5 subSource:4
// mainSource:6 subSource:5
withLatestFromの特徴
withLatestFrom
の特徴を以下に示します。
- サブのObservableから最新の値を取得する
withLatestFrom
は、サブのObservableの最新の値を保持しており、必要に応じてメインのObservableと組み合わせます。
- メインのObservableの発行をトリガーとする
withLatestFrom
は、メインのObservableが値を発行するまで何も出力しません。このため、メインのObservableがトリガーとして機能します。
- サブのObservableが最初の値を発行しないと、メインのObservableが値を発行しても何も起こらない
- サブのObservableが最初の値を発行していない場合、メインのObservableが値を発行しても何も起こりません。初期値を保証したい場合は
startWith
オペレータを組み合わせるのが有効です。
- サブのObservableが最初の値を発行していない場合、メインのObservableが値を発行しても何も起こりません。初期値を保証したい場合は
上記の特徴が分かるサンプルコードをこれから説明します。
値を流し始めると、メインのObservableの発行をトリガーとする
withLatestFrom
は一度動作を開始すると、メインのObservableの値が発行されるたびに後続の処理が行われます。サンプルコードを以下に示します。
import { timer } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
// 1000ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const mainSource$ = timer(1000, 1000);
// 1500ms後に0を出力し、その後2000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const subSource$ = timer(1500, 2000);
// withLatestFromを使用して、2つのObservableの最新の値を組み合わせて新しいObservableを出力する
const combined$ = mainSource$.pipe(withLatestFrom(subSource$));
combined$.subscribe((value) => console.log(value));
// ログ
// [ 1, 0 ]
// [ 2, 0 ]
// [ 3, 1 ]
// [ 4, 1 ]
// [ 5, 2 ]
// [ 6, 2 ]
上記のサンプルコードでは、mainSource$
が値を発行するたびに、最新のsubSource$
の値を取得して処理を行います。このように、値を流し始めると、メインのObservableから値が流された時のみ後続に値を流すのが特徴になっています。
サブのObservableが最初の値を発行しないと、メインのObservableが値を発行しても何も起こらない
以下のサンプルコードは、この動作を示しています。
import { timer } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
// 1000ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const mainSource$ = timer(1000, 1000);
// 3500ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const subSource$ = timer(3500, 1000);
// withLatestFromを使用して、2つのObservableの最新の値を組み合わせて新しいObservableを出力する
const combined$ = mainSource$.pipe(withLatestFrom(subSource$));
combined$.subscribe((value) => console.log(value));
// ログ
// [ 3, 0 ]
// [ 4, 1 ]
// [ 5, 2 ]
// [ 6, 3 ]
// [ 7, 4 ]
上記のサンプルコードでは、subSource$
が少なくとも1つ値を発行した後で、mainSource$
が値を発行すると、組み合わせた結果が出力されます。このように、サブのObservableから1つ以上の値が流されている状態で、メインのObservableから値が流れた時に後続に値を流すのが特徴となっています。
初期値を保証したい場合はstartWith
オペレータを組み合わせるのが有効です。
import { timer } from 'rxjs';
import { startWith, withLatestFrom } from 'rxjs/operators';
// 1000ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const mainSource$ = timer(1000, 1000);
// 3500ms後に0を出力し、その後1000ms秒ごとに「1→2→3→4→・・・」というデータを流すObservable
const subSource$ = timer(3500, 1000).pipe(startWith(0));
// withLatestFromを使用して、2つのObservableの最新の値を組み合わせて新しいObservableを出力する
const combined$ = mainSource$.pipe(withLatestFrom(subSource$));
combined$.subscribe((value) => console.log(value));
// ログ
// [ 0, 0 ]
// [ 1, 0 ]
// [ 2, 0 ]
// [ 3, 0 ]
// [ 4, 1 ]
// [ 5, 2 ]
// [ 6, 3 ]
// [ 7, 4 ]
補足
オペレーターの動作を理解する上で、以下のサイトが非常に便利です。発行されるObservableを移動させたりして、各オペレーターの動作を確認できます。
withLatestFromオペレータとcombineLatestオペレータの違いと使い分け
withLatestFrom
と似たオペレータにcombineLatest
があります。combineLatest
は、複数のObservableを入力として取り、それぞれのObservableの最新の値を組み合わせて新しいObservableを出力するオペレータです。
あわせて読みたい
『combineLatestオペレータ』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 続きを見る【RxJS】combineLatestオペレータとは?「使い方」や「特徴」などを解説!
以下にwithLatestFrom
とcombineLatest
の違いを示します。
withLatestFrom | combineLatest | |
合成可能なObservable数 | 2つ (メインとサブのObservable) | 2つ以上 |
初回値を流す条件 | サブのObservableから1回以上値が流れている状態で、メインのObservableから値が流れた時 | 全てのObservableから少なくとも1回値が流れた時 |
2回目以降の値を流す条件 | メインのObservableが値を流したとき | どれか1つのObservableが値を流したとき |
用途 | メインのObservableに基づいてデータを加工する際に使用 | 複数のObservableの状態を常に最新で追跡したい場合 |
withLatestFrom
とcombineLatest
の使い分けを以下に示します。
withLatestFrom
を使うとき- 2つのObservableを合成する場合において、メインのObservableから値が流れた時のみ値を流したい場合
- 例: ボタンをクリックした際に他の状態(サブObservable)を参照したい
combineLatest
を使うとき- 2つ以上のObservableを合成する場合
- 2つのObservableを合成する場合において、どちらかのObservableから値が流れた時に値を流したい場合
- 例: 複数のフォーム入力状態の最新値をリアルタイムに結合して表示したい。
本記事のまとめ
この記事ではRxJSの『withLatestFromオペレータ』について、以下の内容を説明しました。
withLatestFrom
オペレータとはwithLatestFrom
オペレータの「使い方」withLatestFrom
オペレータの「特徴」withLatestFrom
オペレータとcombineLatest
オペレータの違いと使い分け
お読み頂きありがとうございました。