网址:https://www.learnrxjs.io/lear...
switchMap 和其他扁平化操作符的主要区别在于取消效果。 在每次发射时,先前的内部 observable(您提供的函数的结果)被取消并订阅新的 observable。 您可以通过短语 switch to a new observable 记住这一点。
这对于像预先输入这样的场景非常有效,当新输入到达时,您不再关心先前请求的响应。 在长期存在的内部 observable 可能导致内存泄漏的情况下,这也是一个安全的选择。
但是要小心,您可能希望在每个请求都需要完成的情况下避免使用 switchMap,比如写入数据库的场景。 如果源发出足够快,switchMap 可以取消请求。 在这些情况下,mergeMap 是正确的选项。
看这个例子:
import { interval, fromEvent } from 'rxjs';
import { switchMap } from 'rxjs/operators';
fromEvent(document, 'click')
.pipe(
// restart counter on every click
switchMap(() => interval(1000))
)
.subscribe(console.log);
每次点击屏幕之后,fromEvent issue 出来的 MouseEvent,传入 switchMap 内部,都会重启一个新的时间间隔为1秒的计时器,并且取消之前的定时器。打印如下:
再看一个实现定时器的例子:
import { Component, OnInit } from '@angular/core';
import { interval, fromEvent, merge, empty } from 'rxjs';
import { switchMap, scan, takeWhile, startWith, mapTo } from 'rxjs/operators';
const COUNTDOWN_SECONDS = 1000;
@Component({
selector: 'switchMap-study',
templateUrl: './switchMap.component.html'
})
export class SwitchMapComponent implements OnInit {
ngOnInit(): void {
// elem refs
const remainingLabel = document.getElementById('remaining');
const pauseButton = document.getElementById('pause');
const resumeButton = document.getElementById('resume');
// streams
const interval$ = interval(1000).pipe(mapTo(-1));
const pause$ = fromEvent(pauseButton, 'click').pipe(mapTo(false));
const resume$ = fromEvent(resumeButton, 'click').pipe(mapTo(true));
const timer$ = merge(pause$, resume$).pipe(
startWith(true),
switchMap(val => (val ? interval$ : empty())),
scan((acc, curr) => (curr ? curr + acc : acc), COUNTDOWN_SECONDS),
takeWhile(v => v >= 0)).subscribe((val: any) => (remainingLabel.innerHTML = val));
}
}
<h1>Switch Map Study</h1>
<a href="https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap">Document</a>
<h4>
Time remaining: <span id="remaining"></span>
</h4>
<button id="pause">
Pause Timer
</button>
<button id="resume">
Resume Timer
</button>
刚开始时,startWith true,因此 switchMap 得以继续下去。switchMap 传入的 project,接受的参数 val 是 boolean 类型,返回一个新的 Observable,即 internal$.
注意 empty() 的用法,返回一个类型为 never 的 Observable.
单步调试点了 pause 之后:
第 41 行 unsubscribe 方法,体现了 switchMap 自动取消先前的内部 observable 的特性:
先 cancel,再订阅新的 Observable:
如果返回 empty,就不会再执行后续的 scan 了:
可以把 switchMap 输入参数,即 projection 进行改造:
该 projection 的类型是一个箭头函数,输入参数是一个 boolean,输出是一个新的 Observable.
更多Jerry的原创文章,尽在:"汪子熙":