先取りRxJS v7 (alpha.1)

:warning: この記事は現時点でv7.0.0-alpha.1が出ている、正式リリース前のRxJS v7について扱っている

# RxJS v7の大きめな変更点
- `scheduled` が追加
- v6でdeprecatedだった一部Operatorsの扱い変更
- 型の強化
- `TestScheduler` の変化

# `scheduled` が追加
`scheduled` はCreation Functionのひとつ。次のようにimportして使う。

```ts
import { scheduled } from 'rxjs';
```

`scheduled` の追加に伴い、一部Creation FunctionsおよびOperatorsにて、schedulerを受け取るシグネチャが（v6.5の時点で）deprecatedになっている。書き換えが必要になるが、基本的にスケジューラーを酷使していなければ、後述するパターンはそもそも必要ないことを覚えておいてほしい。

対象と動作を維持した書き換え例は次の通り：

## Creation Functions
### `of`

```ts
// before(deprecated)
import { of, asyncScheduler } from 'rxjs';

of(1, 2, 3, asyncScheduler).subscribe(console.log);

// after
import { scheduled, asyncScheduler } from 'rxjs';

scheduled([1, 2, 3], asyncScheduler).subscribe(console.log);
```

### `from`

```ts
// before(deprecated)
import { from, asyncScheduler } from 'rxjs';

from([1, 2, 3], asyncScheduler).subscribe(console.log);

// after
import { scheduled, asyncScheduler } from 'rxjs';

scheduled([1, 2, 3], asyncScheduler).subscribe(console.log);
```

### `concat`

```ts
// before(deprecated)
import { of, concat, asyncScheduler } from 'rxjs';

concat(of(1), of(2), of(3), asyncScheduler).subscribe(console.log);

// after
import { of, scheduled, asyncScheduler } from 'rxjs';
import { concatAll } from 'rxjs/operators';

scheduled([of(1), of(2), of(3)], asyncScheduler)
  .pipe(
    concatAll()
  )
  .subscribe(console.log);
```

### `merge`

```ts
// before(deprecated)
import { of, merge, asyncScheduler } from 'rxjs';

merge(of(1), of(2), of(3), asyncScheduler).subscribe(console.log);

// after
import { of, scheduled, asyncScheduler } from 'rxjs';
import { mergeAll } from 'rxjs/operators';

scheduled([of(1), of(2), of(3)], asyncScheduler)
  .pipe(
    mergeAll()
  )
  .subscribe(console.log);
```

### `combineLatest`

```ts
// before(deprecated)
import { of, combineLatest, asyncScheduler } from 'rxjs';

combineLatest(of(1), of(2), of(3), asyncScheduler).subscribe(console.log);

// after
import { of, scheduled, asyncScheduler } from 'rxjs';
import { combineAll } from 'rxjs/operators';

scheduled([of(1), of(2), of(3)], asyncScheduler)
  .pipe(
    combineAll()
  )
  .subscribe(console.log);
```

この記述は確かに等価だ。しかしもともとの動きとして、subscribeの実行がcombineLatest/combineAllするObservableの数+1回分、与えたSchedulerの時間で遅らされていることに注意を要する。

ハードにSchedulerを使い込んでいて非同期の実行タイミングに厳しい場合は、 `scheduled` を使わずに `observeOn` / `subscribeOn` を用いてemissionとsubscriptionのタイミングを操作してやる必要があるかもしれない。

## Operators
### `startWith`

```ts
// before(deprecated)
import { of, concat, asyncScheduler } from 'rxjs';
import { startWith } from 'rxjs/operators';

of(3)
  .pipe(
    startWith(1, 2, asyncScheduler)
  )
  .subscribe(console.log)

// after
import { of, scheduled, asyncScheduler } from 'rxjs';
import { concatAll } from 'rxjs/operators';

scheduled([[1, 2], of(3)], asyncScheduler)
  .pipe(
    concatAll()
  )
  .subscribe(console.log);
```

### `endWith`

```ts
// before(deprecated)
import { of, concat, asyncScheduler } from 'rxjs';
import { endWith } from 'rxjs/operators';

of(1)
  .pipe(
    endWith(2, 3, asyncScheduler)
  )
  .subscribe(console.log)

// after
import { of, scheduled, asyncScheduler } from 'rxjs';
import { concatAll } from 'rxjs/operators';

from([of(1), scheduled([2, 3], asyncScheduler)])
  .pipe(
    concatAll()
  )
  .subscribe(console.log);
```

`startWith` `endWith` 共に正確に実行タイミングが等価になるような実装を示しているが、そこまで正確でなくてよい場合はうまくやってもらいたい。

# v6でdeprecatedだった一部Operatorsの扱い変更

Creation FunctionsとOperatorsで同名の実装が存在した次のOperatorsは、リネームと差別化が図られる：

- `concat` -> `concatWith`
- `merge` -> `mergeWith`
- `zip` -> `zipWith`
- `combineLatest` -> `combineLatestWith`

リネームは上記の通りに `-With` を補った名前になる。差別化は後述する形の改善と、scheduler引数およびresultSelector引数（あれば）の先んじての削除が入る。

# 型の強化
これまで、Observableを引数に受け取るような関数の型は、長大なオーバーロード定義によって限られた数の引数にのみ適切なUnion型やTuple型を返していたが、これが任意個の引数に対して動作するようになる。

see. [ReactiveX/rxjs#5254](https://github.com/ReactiveX/rxjs/pull/5254), [ReactiveX/rxjs#5257](https://github.com/ReactiveX/rxjs/pull/5257)


ex.

```ts
import { combineLatestWith } from 'rxjs/operators';

const a = of(1, 2, 3);
const b = of('a', 'b', 'c');
const c = of('d', 'e', 'f');
const d = of('g', 'h', 'i');
const e = of('j', 'k', 'l');
const f = of('m', 'n', 'o');
const g = of('p', 'q', 'r');
const res = a.pipe(combineLatestWith(b, c, d, e, f, g))

// before
res; // $ExpectType Observable<(string | number)[]>

// after
res; // $ExpectType Observable<[number, string, string, string, string, string, string]>
```

_from [ReactiveX/rxjs#5257](https://github.com/ReactiveX/rxjs/pull/5257)_

# `TestScheduler` の変化

v6時点から特定のメソッドを用いることで使えた、 `frameTimeFactor` と `maxFrameCount` に関する変化は継続しており、テストには `run` メソッドを用いておけば問題ない。

`run` に関しては以前記事を書いているので移譲する。 → https://qiita.com/berlysia/items/dad98488c4bdde938ef3

# `rxjs-compat` の削除

v5からv6への移行に際して、Pipeable Operatorやインポートパス整理など大きな変化を吸収するためのモジュールが存在したが、v7を迎えるに当たって消える予定がある。

内容はドキュメントを見よ: https://github.com/ReactiveX/rxjs/blob/473a66813c841ee2763eac186d0ac47ea1737815/docs_app/content/guide/v6/migration.md#drop-compat

# 他、v6でのdeprecatedな実装の削除

ドキュメントを見よ: https://github.com/ReactiveX/rxjs/blob/473a66813c841ee2763eac186d0ac47ea1737815/docs_app/content/guide/v6/migration.md#deprecations

# 他、v7に向けて
まだ数多くのPRが出ており、議論も行われているようだが、一番大きな変更は今のところ `scheduled` の追加周りだろうと筆者は見ている。

型周りの改善もまだまだ控えている様子なので、ユーザー側として実装の変化はスケジューラー周りが主でありながら、総じての使い勝手はよくなるものと期待している。

# 参考資料

- https://docs.google.com/presentation/d/1nCg_GNdtd4mysyxj8jEpGpKE0X1MirH8c5qIKu6udXw/edit
- [ReactiveX/rxjs#4776](https://github.com/ReactiveX/rxjs/issues/4776)