switchMap

签名: switchMap(project: function: Observable, resultSelector: function(outerValue, innerValue, outerIndex, innerIndex): any): Observable

映射成 observable,完成前一个内部 observable,发出值。


:bulb: 如果你想要维护多个内部 subscription 的话, 请尝试 mergeMap

:bulb: 此操作符通常被认为是 mergeMap 的安全版本!

:bulb: 此操作符可以取消正在进行中的网络请求!


为什么使用 switchMap

switchMap 和其他打平操作符的主要区别是它具有取消效果。在每次发出时,会取消前一个内部 observable (你所提供函数的结果) 的订阅,然后订阅一个新的 observable 。你可以通过短语切换成一个新的 observable来记忆它。

它能在像 typeaheads 这样的场景下完美使用,当有新的输入时便不再关心之前请求的响应结果。在内部 observable 长期存活可能会导致内存泄露的情况下,这也是一种安全的选择,例如,如果你使用 mergeMap 和 interval,并忘记正确处理内部订阅。记住,switchMap 同一时间只维护一个内部订阅,在示例 1中可以清楚出看到这一点。

不过要小心,在每个请求都需要完成的情况下,考虑写数据库,你可能要避免使用 switchMap 。如果源 observable 发出速度足够快的话,switchMap 可以取消请求。在这些场景中,mergeMap 是正确的选择。

switchMap - 图4

示例

示例 1: 每5秒重新启动 interval

( StackBlitz |
jsBin |
jsFiddle )

  1. import { timer } from 'rxjs/observable/timer';
  2. import { interval } from 'rxjs/observable/interval';
  3. import { switchMap } from 'rxjs/operators';
  4. // 立即发出值, 然后每5秒发出值
  5. const source = timer(0, 5000);
  6. // 当 source 发出值时切换到新的内部 observable,发出新的内部 observable 所发出的值
  7. const example = source.pipe(switchMap(() => interval(500)));
  8. // 输出: 0,1,2,3,4,5,6,7,8,9...0,1,2,3,4,5,6,7,8
  9. const subscribe = example.subscribe(val => console.log(val));
示例 2: 每次点击时重置

( StackBlitz |
jsBin |
jsFiddle )

  1. import { fromEvent } from 'rxjs/observable/fromEvent';
  2. import { interval } from 'rxjs/observable/interval';
  3. import { switchMap, mapTo } from 'rxjs/operators';
  4. // 发出每次点击
  5. const source = fromEvent(document, 'click');
  6. // 如果3秒内发生了另一次点击,则消息不会被发出
  7. const example = source.pipe(
  8. switchMap(val => interval(3000).pipe(mapTo('Hello, I made it!')))
  9. );
  10. // (点击)...3s...'Hello I made it!'...(点击)...2s(点击)...
  11. const subscribe = example.subscribe(val => console.log(val));
示例 3: 使用 resultSelector 函数

( StackBlitz |
jsBin |
jsFiddle )

  1. import { timer } from 'rxjs/observable/timer';
  2. import { interval } from 'rxjs/observable/interval';
  3. import { switchMap } from 'rxjs/operators';
  4. // 立即发出值, 然后每5秒发出值
  5. const source = timer(0, 5000);
  6. // 当 source 发出值时切换到新的内部 observable,调用投射函数并发出值
  7. const example = source.pipe(
  8. switchMap(
  9. _ => interval(2000),
  10. (outerValue, innerValue, outerIndex, innerIndex) => ({
  11. outerValue,
  12. innerValue,
  13. outerIndex,
  14. innerIndex
  15. })
  16. )
  17. );
  18. /*
  19. 输出:
  20. {outerValue: 0, innerValue: 0, outerIndex: 0, innerIndex: 0}
  21. {outerValue: 0, innerValue: 1, outerIndex: 0, innerIndex: 1}
  22. {outerValue: 1, innerValue: 0, outerIndex: 1, innerIndex: 0}
  23. {outerValue: 1, innerValue: 1, outerIndex: 1, innerIndex: 1}
  24. */
  25. const subscribe = example.subscribe(val => console.log(val));
示例 4: 使用 switchMap 的倒计时定时器

( StackBlitz |
jsBin |
jsFiddle )

  1. import { interval } from 'rxjs/observable/interval';
  2. import { fromEvent } from 'rxjs/observable/fromEvent';
  3. import { merge } from 'rxjs/observable/merge';
  4. import { empty } from 'rxjs/observable/empty';
  5. import { switchMap, scan, takeWhile, startWith, mapTo } from 'rxjs/operators';
  6. const countdownSeconds = 10;
  7. const setHTML = id => val => (document.getElementById(id).innerHTML = val);
  8. const pauseButton = document.getElementById('pause');
  9. const resumeButton = document.getElementById('resume');
  10. const interval$ = interval(1000).pipe(mapTo(-1));
  11. const pause$ = fromEvent(pauseButton, 'click').pipe(mapTo(false));
  12. const resume$ = fromEvent(resumeButton, 'click').pipe(mapTo(true));
  13. const timer$ = merge(pause$, resume$)
  14. .pipe(
  15. startWith(interval$),
  16. switchMap(val => (val ? interval$ : empty())),
  17. scan((acc, curr) => (curr ? curr + acc : acc), countdownSeconds),
  18. takeWhile(v => v >= 0)
  19. )
  20. .subscribe(setHTML('remaining'));
HTML
  1. <h4>
  2. Time remaining: <span id="remaining"></span>
  3. </h4>
  4. <button id="pause">
  5. Pause Timer
  6. </button>
  7. <button id="resume">
  8. Resume Timer
  9. </button>

相关食谱

其他资源


:file_folder: 源码: https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/switchMap.ts