RxJS のxxxMap の動作確認して理解を深めるまで

## 前置き
RxJS のxxxMap についてちゃんと理解できていなかったので、動作確認したときのメモ。  
確認したのは下記4種類  
- mergeMap
- switchMap
- concatMap
- exhaustMap

## 本編
### ソースコード
↓がソースコードで引数`xxxMap`に`mergeMap`とか渡して実行した。
```
  // eslint-disable-next-line
  executeXxxMap(xxxMap: any, reverse: boolean) {
    console.log(xxxMap.toString());
    const cnt = 6;
    const now = Date.now();
    const getTime = () => {
      return format(
        // 関数実行時刻より経過した分:秒.ミリ秒を取得
        Date.now() - now,
        'mm:ss.S',
      );
    };
    const doSomething = (n: number) => {
      return of(n).pipe(
        // 何か重い処理の代わり
        // ログ出力
        tap((n) =>
          console.log(`time: ${getTime()}, input: ${n}, doSomething start`),
        ),
        // n === 0 の場合は待機なし
        // それ以外の場合、
        // reverse === true の時、(cnt - n) * 1.5秒待機
        // reverse === false の時、n * 1.5秒待機
        delay(n === 0 ? 0 : reverse ? (cnt - n) * 1500 : n * 1500),
        // ログ出力
        tap((n) =>
          console.log(`time: ${getTime()}, output: ${n}, doSomething end`),
        ),
      );
    };
    interval(1000)
      .pipe(
        // 1秒毎に値を流す
        // 最初のcnt個だけ値を取得
        take(cnt),
        // ログ出力
        tap((x) => console.log(`time: ${getTime()}, input: ${x}`)),
        // xxxMap処理
        xxxMap((x: number) => {
          console.log(`time: ${getTime()}, input: ${x}, xxxMap start`);
          return doSomething(x);
        }),
        // ログ出力
        tap((x) => console.log(`time: ${getTime()}, output: ${x}`)),
      )
      .subscribe({
        next: (x) => console.log(`time: ${getTime()}, next: ${x}`),
        error: (e) => console.error(e),
        complete: () => console.log('complete'),
      });
  }
```

### mergeMap
特にひねりとかなく、上流から流れてきた値を素直に処理していく。  
注意としては、入出力の順番が前後する可能性があること。  
みんな平等に対応するいい人。マルチタスク。  

`executeXxxMap(mergeMap, true)`を実行した結果が↓。
```
time: 00:01.0, input: 0
time: 00:01.0, input: 0, xxxMap start
time: 00:01.0, input: 0, doSomething start
time: 00:01.0, output: 0, doSomething end
time: 00:01.0, output: 0
time: 00:01.0, next: 0
time: 00:02.0, input: 1
time: 00:02.0, input: 1, xxxMap start
time: 00:02.0, input: 1, doSomething start
time: 00:03.0, input: 2
time: 00:03.0, input: 2, xxxMap start
time: 00:03.0, input: 2, doSomething start
time: 00:04.0, input: 3
time: 00:04.0, input: 3, xxxMap start
time: 00:04.0, input: 3, doSomething start
time: 00:05.0, input: 4
time: 00:05.0, input: 4, xxxMap start
time: 00:05.0, input: 4, doSomething start
time: 00:06.0, input: 5
time: 00:06.0, input: 5, xxxMap start
time: 00:06.0, input: 5, doSomething start
time: 00:07.5, output: 5, doSomething end
time: 00:07.5, output: 5
time: 00:07.5, next: 5
time: 00:08.0, output: 4, doSomething end
time: 00:08.0, output: 4
time: 00:08.0, next: 4
time: 00:08.5, output: 3, doSomething end
time: 00:08.5, output: 3
time: 00:08.5, next: 3
time: 00:09.0, output: 2, doSomething end
time: 00:09.0, output: 2
time: 00:09.0, next: 2
time: 00:09.5, output: 1, doSomething end
time: 00:09.5, output: 1
time: 00:09.5, next: 1
complete
```

### switchMap
前の値に対する処理が終わっていない状況で次の値が流れてきた場合、  
前の値に対する処理が打ち切られ、次の値に対する処理が開始する。  
作業中にタスク振ると今までやってたタスクを忘れる人。シングルタスク。  

`executeXxxMap(switchMap, true)`を実行した結果が↓。
```
time: 00:01.0, input: 0
time: 00:01.0, input: 0, xxxMap start
time: 00:01.0, input: 0, doSomething start
time: 00:01.0, output: 0, doSomething end
time: 00:01.0, output: 0
time: 00:01.0, next: 0
time: 00:02.0, input: 1
time: 00:02.0, input: 1, xxxMap start
time: 00:02.0, input: 1, doSomething start
time: 00:03.0, input: 2
time: 00:03.0, input: 2, xxxMap start
time: 00:03.0, input: 2, doSomething start
time: 00:04.0, input: 3
time: 00:04.0, input: 3, xxxMap start
time: 00:04.0, input: 3, doSomething start
time: 00:05.0, input: 4
time: 00:05.0, input: 4, xxxMap start
time: 00:05.0, input: 4, doSomething start
time: 00:06.0, input: 5
time: 00:06.0, input: 5, xxxMap start
time: 00:06.0, input: 5, doSomething start
time: 00:07.5, output: 5, doSomething end
time: 00:07.5, output: 5
time: 00:07.5, next: 5
complete
```

### concatMap
前の値に対する処理が終わっていない状況で次の値が流れてきた場合、  
前の値に対する処理が終了するまで次の値に対する処理を待機する。  
前の値に対する処理が終了したら、次の値に対する処理を開始する。  
順番通りに対応する行儀のいい人。シングルタスク。  

`executeXxxMap(concatMap, true)`を実行した結果が↓。
```
time: 00:01.0, input: 0
time: 00:01.0, input: 0, xxxMap start
time: 00:01.0, input: 0, doSomething start
time: 00:01.0, output: 0, doSomething end
time: 00:01.0, output: 0
time: 00:01.0, next: 0
time: 00:02.0, input: 1
time: 00:02.0, input: 1, xxxMap start
time: 00:02.0, input: 1, doSomething start
time: 00:03.0, input: 2
time: 00:04.0, input: 3
time: 00:05.0, input: 4
time: 00:06.0, input: 5
time: 00:09.5, output: 1, doSomething end
time: 00:09.5, output: 1
time: 00:09.5, next: 1
time: 00:09.5, input: 2, xxxMap start
time: 00:09.5, input: 2, doSomething start
time: 00:15.5, output: 2, doSomething end
time: 00:15.5, output: 2
time: 00:15.5, next: 2
time: 00:15.5, input: 3, xxxMap start
time: 00:15.5, input: 3, doSomething start
time: 00:20.0, output: 3, doSomething end
time: 00:20.0, output: 3
time: 00:20.0, next: 3
time: 00:20.0, input: 4, xxxMap start
time: 00:20.0, input: 4, doSomething start
time: 00:23.0, output: 4, doSomething end
time: 00:23.0, output: 4
time: 00:23.0, next: 4
time: 00:23.0, input: 5, xxxMap start
time: 00:23.0, input: 5, doSomething start
time: 00:24.5, output: 5, doSomething end
time: 00:24.5, output: 5
time: 00:24.5, next: 5
complete
```

### exhaustMap
前の値に対する処理が終わっていない状況で次の値が流れてきた場合、  
次の値は無視する。  
switchMapの逆。  
作業中にタスク振ると無視する人。シングルタスク。  

`executeXxxMap(exhaustMap, false)`を実行した結果が↓。
```
time: 00:01.0, input: 0
time: 00:01.0, input: 0, xxxMap start
time: 00:01.0, input: 0, doSomething start
time: 00:01.0, output: 0, doSomething end
time: 00:01.0, output: 0
time: 00:01.0, next: 0
time: 00:02.0, input: 1
time: 00:02.0, input: 1, xxxMap start
time: 00:02.0, input: 1, doSomething start
time: 00:03.0, input: 2
time: 00:03.5, output: 1, doSomething end
time: 00:03.5, output: 1
time: 00:03.5, next: 1
time: 00:04.0, input: 3
time: 00:04.0, input: 3, xxxMap start
time: 00:04.0, input: 3, doSomething start
time: 00:05.0, input: 4
time: 00:06.0, input: 5
time: 00:08.5, output: 3, doSomething end
time: 00:08.5, output: 3
time: 00:08.5, next: 3
complete
```

### 追記(ここから)
↑はxxxMap 内で使用しているObservable が単発(値を1個だけ流してすぐにcomplete する時)の場合である。  
単発じゃない場合はどうなるか調べた。  
ソースは長くなってしまったので省略します。  
詳細はこちら  

https://github.com/magurouhiru/mysite/commit/e931eaaa5f23ed630874874b5250316a5b4908e1

結果としては上で個別調べた結果と同じではある。  
ただし、上の「処理が終わっていない」は、Observable 使用時についてはふわっとした記載だった。  
そして、「Observable がcomplete していない」が具体的な記載だと考え直した。  

まあ、xxxMap はObservable 以外にもPromise とか使えるので元の記載も間違っていないとは思う。  
ということで、ご勘弁を。  

追記(ここまで)

## 感想
楽しかった。  
あと、一度理解したつもりだったけど、ちゃんとハラオチしてなかったと実感した。  
(多分また忘れる気がするけどこの記事見たら思い出せるかな？)  


今回の記事は以上です。  

前回記事はこちら  

https://qiita.com/magurouhiru/items/afa7eaba4562f208a984

詳細見たい人はこちら  

https://github.com/magurouhiru/mysite/pull/47

https://magurouhiru.github.io/mysite/#/study/RxjsXxxMap