RxJS 6: オペレータをつくってみる

[RxJS 6公式サイト](https://rxjs-dev.firebaseapp.com)はベータ段階のようで、まだチュートリアル([Overview](https://rxjs-dev.firebaseapp.com/guide/overview))の情報が「Introduction」しかありません。そこで、バージョン5の「[Overview](http://reactivex.io/rxjs/manual/index.html)」を下じきに、バージョン6に対応させたうえ、コードや解説も改めて「[RxJS 6入門](http://fumiononaka.com/Business/index.html#RxJS_6)」01-07を書きました。

中でも「[RxJS 6入門 06: Observableをつくる関数とオペレータ](http://fumiononaka.com/Business/html5/FN1806004.html)」は、ほぼ書き直しでした。そこで本稿では、RxJS 6のオペレータの使い方をかいつまんでおさらいし、簡単なオペレータを自作してみます。オペレータの仕組みを理解するのに役立つでしょう。

# ライブラリをCDNから読み込む
今回は手軽に試せるように、RxJSライブラリはCDNから読み込みます。GitHubの「[Installation and Usage](https://github.com/ReactiveX/rxjs#installation-and-usage)」の「[CDN](https://github.com/ReactiveX/rxjs#cdn)」にしたがって、`<script>`要素につぎのように`src`属性を定めてください。


```html
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
```

> 前にあった[RxJS 5.5のCDN](https://github.com/ReactiveX/rxjs/tree/stable#cdn)は消されたようです。RxJS 6公式サイトの「[Installation Instructions](https://rxjs-dev.firebaseapp.com/guide/installation)」のページも「[CDN](https://rxjs-dev.firebaseapp.com/guide/installation#cdn)」の情報がバージョン5.0から更新されていません。


# クラスと関数およびオペレータの読み込みパス
バージョン6でまず気をつけなければならないのは、ライブラリの読み込みパスです。おもなクラスと関数は名前空間が`rxjs`にまとめられ、オペレータは`rxjs.operators`になりました(「[Installation and Usage](https://github.com/ReactiveX/rxjs#installation-and-usage)」参照)。

```javascript
const {Observable, Subject, ReplaySubject, from, of, range} = rxjs;
const {map, filter, switchMap} = rxjs.operators;
```

npmで[ECMAScript 2015(ES6)](https://github.com/ReactiveX/rxjs#es6-via-npm)や[CommonJS](https://github.com/ReactiveX/rxjs#commonjs-via-npm)を使う場合は、それぞれリンク先のGitHubのページをご覧ください(なお「[Import paths](https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md#import-paths)」参照)。


# pipe()メソッドでオペレータを使う
RxJS 6では、オペレータは[`pipe()`メソッド](https://rxjs-dev.firebaseapp.com/api/index/pipe)の引数に渡さなければなりません。このメソッドそのものは、バージョン5.5で備わったものです(「[Operator pipe syntax](https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md#operator-pipe-syntax)」)。けれどこのときは、`Observable`のメソッドとしてオペレータを呼び出すバージョン5の書き方もできました。それが`pipe()`メソッド一択になったということです。

つぎのコードは、`from()`関数が配列からつくった`Observable`に、`pipe()`メソッドで`map()`オペレータを適用しています。送られた値はオペレータが2乗して、コンソールに出力するという流れです。

```javascript
const {from} = rxjs;
const {map} = rxjs.operators;
const observable = from([1, 2, 3, 4, 5])
.pipe(
	map((x) => x ** 2)
)
.subscribe(
	(x) => console.log(`value: ${x}`)
);
```

```console
value: 1
value: 4
value: 9
value: 16
value: 25
```

- [`from()`](https://rxjs-dev.firebaseapp.com/api/index/from): 配列や配列型(array-like)オブジェクト、`Promise`、反復可能(iterable)オブジェクト、あるいは`Observable`から新たな`Observable`をつくって返します。
- [`map()`](https://rxjs-dev.firebaseapp.com/api/operators/map): もとの`Observable`から送られた値に引数のプロジェクト関数を適用し、その戻り値が新たな値となる`Observable`にして返します。
- [`**`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#べき乗_(**)): べき乗演算子(ECMAScript 2016)。左オペランド(被演算子)を右オペランドで累乗します。
- [``](https://developer.mozilla.org/docs/Web/JavaScript/Reference/template_strings): テンプレート文字列(ECMAScript 2015)。組み込み式`${}`を扱うことができる文字列リテラルです。



# オペレータの働きをする関数
オペレータが何をしているのか、簡単な関数をつくって確かめてみましょう。つぎの関数(square)は、送られた値を2乘するオペレータです。引数に`Observable`を受け取って、新たな`Observable`にして返します。その戻り値のオブジェクトに、オペレータの処理(2乗)をサブスクライブするのです。

```javascript
const {Observable, from} = rxjs;
function square(input) {
	return Observable.create((observer) => {
		input.subscribe({
			next(value) {observer.next(value ** 2);},
			error(err) {observer.error(err);},
			complete() {observer.complete();}
		});
	});
}
const input = from([1, 2, 3, 4, 5]);
square(input)
.subscribe(
	(x) => console.log(`value: ${x}`)
);
```

もっとも、この関数はオペレータの働きを示しただけです。標準のオペレータのように`pipe()`の引数には渡せません。


# 関数をpipe()メソッドに渡す
前掲の関数(square())は、引数に`Observable`を受け取って、新たな`Observable`にして返しました。`pipe()`メソッドがやっているのは、オペレータに`Observable`を渡すことです。そのため、引数は`Observable`が受け取れる関数を返さなければならないのです。

したがって、つぎのように`Observable`を受け取って前掲関数が返される無名関数にすれば、`pipe()`メソッドの引数に与えられます。

```javascript
// const input = 
from([1, 2, 3, 4, 5])  // ;
.pipe(
	(input) => square(input)
)
// square(input)
.subscribe(
	(x) => console.log(`value: ${x}`)
);
```


# pipe()メソッドに渡せるオペレータを定める
つまり、前掲関数(square())は入れ子にして、つぎのように関数を戻り値にすれば標準のオペレータと同じ使い方ができるのです。

```javascript
// function square(input) {
function square() {
	return (input) =>
	/* return */ Observable.create((observer) => {
		input.subscribe({
			next(value) {observer.next(value ** 2);},
			error(err) {observer.error(err);},
			complete() {observer.complete();}
		});
	});
}
from([1, 2, 3, 4, 5])
.pipe(
	// (input) => square(input)
	square()
)
.subscribe(
	(x) => console.log(`value: ${x}`)
);
```

オペレータをつくるというお題は、RxJSドキュメントの「[Pipeable Operators](https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md)」に「[Build Your Own Operators Easily](https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#build-your-own-operators-easily)」として例が掲げられています。ご興味があればご覧ください。

### RxJS 6入門
- 01: [RxJSを使ってみる](http://www.fumiononaka.com/Business/html5/FN1805013.html)
- 02: [Observable](http://www.fumiononaka.com/Business/html5/FN1805014.html)
- 03: [Observer](http://www.fumiononaka.com/Business/html5/FN1806001.html)
- 04: [Subscription](http://www.fumiononaka.com/Business/html5/FN1806002.html)
- 05: [Subject](http://www.fumiononaka.com/Business/html5/FN1806003.html)
- 06: [Observableをつくる関数とオペレータ](http://www.fumiononaka.com/Business/html5/FN1806004.html)
- 07: [Scheduler](http://www.fumiononaka.com/Business/html5/FN1806005.html)