LoginSignup
6

More than 3 years have passed since last update.

RxJS: map, concatMap, mergeMap, switchMap, exhaustMap の動作を理解する

Posted at

サンプルコード書いたら大体理解できた。

利用シーンのイメージ:

  • map: 値の変換操作
  • concatMap: 同期処理など、キューイングして直列実行したい処理。
  • mergeMap: TODOのDone操作など、並列実行したい処理。
  • switchMap: リアルタイム検索のキーボード入力から検索する処理など、後続の処理のみ優先させたい場合。
  • exhaustMap: フォームのサブミットなど、多重実行されたくない処理。
import { of } from 'rxjs';
import {
  concatMap,
  exhaustMap,
  map,
  mergeMap,
  switchMap,
} from 'rxjs/operators';

const sleep = (ms: number) => {
  return new Promise(resolve => {
    console.log('sleep: start');
    setTimeout(() => {
      console.log('sleep: end');
      resolve();
    }, ms);
  });
};

const array = [1, 2, 3];

/**
 * map
 * * Promiseは展開されない
 * * 配列操作と同じ
 */
const mapExample = () => {
  of(...array)
    .pipe(
      map(i => {
        return sleep(1000).then(() => i * 2);
      }),
    )
    .subscribe(i => console.log(i));
};

// 出力:
// Promise { <pending> }
// Promise { <pending> }
// Promise { <pending> }

/**
 * concatMap
 * * Promiseが展開される
 * * 連続で値が流れてきた場合は直列で実行される。
 */
const concatMapExample = () => {
  of(...array)
    .pipe(
      concatMap(i => {
        return sleep(1000).then(() => i * 2);
      }),
    )
    .subscribe(i => console.log(i));
};

// 出力:
// sleep: start
// sleep: end
// 2
// sleep: start
// sleep: end
// 4
// sleep: start
// sleep: end
// 6

/**
 * mergeMap
 * * Promiseが展開される
 * * 連続で値が流れてきた場合は並列で実行される。
 */
const mergeMapExample = () => {
  of(...array)
    .pipe(
      mergeMap(i => {
        return sleep(1000).then(() => i * 2);
      }),
    )
    .subscribe(i => console.log(i));
};

// 出力:
// sleep: start
// sleep: start
// sleep: start
// sleep: end
// 2
// sleep: end
// sleep: end
// 4
// 6

/**
 * switchMap
 * * Promiseが展開される
 * * 連続で値が流れてきた場合は並列で実行され、ストリームには最後の結果しか流れない。
 */
const switchMapExample = () => {
  of(...array)
    .pipe(
      switchMap(i => {
        return sleep(1000).then(() => i * 2);
      }),
    )
    .subscribe(i => console.log(i));
};

// 出力:
// sleep: start
// sleep: start
// sleep: start
// sleep: end
// sleep: end
// sleep: end
// 6

/**
 * exhaustMap
 * * Promiseが展開される
 * * 連続で値が流れてきた場合は最初のPromiseのみ実行され、実行中は後続の値を切り捨てる。
 */
const exhaustMapExample = () => {
  of(...array)
    .pipe(
      exhaustMap(i => {
        return sleep(1000).then(() => i * 2);
      }),
    )
    .subscribe(i => console.log(i));
};

// 出力:
// sleep: start
// sleep: end
// 2

(async () => {
  // 動作確認はコメントを切り替える
  mapExample();
  // switchMapExample();
  // mergeMapExample();
  // concatMapExample();
  // exhaustMapExample();
})();

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6