カテゴリー
【Rxjs活用講座】deferで作るステイトフルでPipeableなカスタムオペレーターの作り方
※ 当ページには【広告/PR】を含む場合があります。
2020/08/26
2022/10/05
前回の話題で
今回は
「defer」
内容としては、
リアクティブな関数型プログラミングの基礎 関数型リアクティブプログラミング (Programmer's SELECTION)
モチベーション
Rxjs標準の
大体はこれらを組み合わせてパイプライン処理を合成する程度で事足りるのですが、複数のプロジェクトで利用するような自家製ライブラリで共通の処理を持たせたい場合があります。
再利用性を考慮して、カスタムPipeableオペレーターを作っておくと便利です。
例えばイメージとしては、
import { Observable } from 'rxjs';
const something$: Observable<any> = ... // Implement an observable
something$.pipe(
myAwesomePipe() // Custom Pipeable
).subscribe(res => console.log(res))
上のケースのように、出力側に流れてくる何かが分かっているような
something$
myAwesomePipe
毎回面倒な標準パイプ関数の合成を作らなくても使い回しができるのが大きな利点です。
リアクティブな関数型プログラミングの基礎 関数型リアクティブプログラミング (Programmer's SELECTION)
Pipeableと純粋関数
カスタムオペレーターの作成手順の話の前に、関数型プログラミングで言うところの
純粋関数(Pure function)
純粋関数を理解すると、rxjsをベースにした関数型プログラミングにももっと親近感が沸いてくる(?)気がします。
純粋関数を本当に理解する上で、集合路のような抽象数学の分野からの説明が必要なのですが、当ブログ記事程度ではとても収まりきれない内容になるので、今回は概要だけ浅くすくって要点だけまとめます。
純粋関数をjavascriptで取り扱うお話をより深堀したい方は、
このブログのお言葉を借りると、rxjsのパイプオペレーターを自作する上で覚えておきたい用語として、
+ 純粋関数:
数学的な関数
+ 不純な関数:
数学的でない関数
+ 参照透過性:
関数の唯一性
+ 副作用:
出力の外部依存性、
もしくは関数外部への出力、
もしくはグローバル変数の操作
などが出てきます。
このブログの中では何が
数学的
ざっくりいうと純粋関数は次のような
副作用
+ 外部からの入力:
グローバル変数やdocument.getElementByIdなどの関数の
引数以外の入力に出力が依存している
+ 外部への出力:
console.logなどで関数外部への出力している
+ 外部の操作:
グローバル変数に対する代入などを操作している
これらを内部でやっている関数は全て不純な関数であり、純粋関数では無くなってしまいます。
純粋関数が保証される場合のプログラミングに嬉しいことがあるかと言うと、以下に挙げられます。
+ コード可読性の向上
+ ログやデバック機能の実装
+ エラーハンドリング
今回取り上げる話題であるPipeableなrxjsオペレーターには、
Observableを入力し、Observableを出力する、純粋関数
これを簡単なコード式で表すと、
const customOperator = () => (source: Observable<any>) => new Observable<any>()
といったパターンになります。
実際はrxjs標準のオペレーターは
純粋関数
ですので、例えば以下のカスタムオペレーターも純粋関数です。
import { map } from 'rxjs';
const myPow = (n: number) => map(x => Math.pow(x, n));
ただこれだと、用法として
map
これを解決するのに使えるのが、
defer
リアクティブな関数型プログラミングの基礎 関数型リアクティブプログラミング (Programmer's SELECTION)
deferによる複数のオペレーターの合成
defer
この特性により、パイプ関数をチェーン合成したカスタムオペレーターが例えば以下のように作成出来ます。
const statefulPipe = () => (source: Observable<any>) => defer(() => source.pipe(
map( /* ...do something with state */ ),
tap( /* ...do something with state */ ),
switchMap( /* ...do something with state */ ),
filter( /* ...do something with state */ ),
// ...
));
これで、通常のrxjsオペレーターのように取り扱いできるようになります。
of(1).pipe(
stateful()
).subscribe(
res => console.log(res)
);
リアクティブな関数型プログラミングの基礎 関数型リアクティブプログラミング (Programmer's SELECTION)
まとめ
カスタムオペレーターを作って、自作のライブラリ化を進めておくと、入力の対象として同一の集合に属するものに対して再利用可能となり、自分のアセットとしていざと言うときに使うことが出来ます。
余裕があれば、より複雑な処理を一括処理できるカスタムオペレーターを引き出しに貯め込んでおくと、ふとした時に役立つこと請け合いです。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー