RxJs SwitchMap 学习笔记
网址:https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap
The main difference between switchMap and other flattening operators is the cancelling effect. On each emission the previous inner observable (the result of the function you supplied) is cancelled and the new observable is subscribed. You can remember this by the phrase switch to a new observable.
switchMap 和其他扁平化操作符的主要区别在于取消效果。 在每次发射时,先前的内部 observable(您提供的函数的结果)被取消并订阅新的 observable。 您可以通过短语 switch to a new observable 记住这一点。
This works perfectly for scenarios like typeaheads where you are no longer concerned with the response of the previous request when a new input arrives. This also is a safe option in situations where a long lived inner observable could cause memory leaks, for instance if you used mergeMap with an interval and forgot to properly dispose of inner subscriptions. Remember, switchMap maintains only one inner subscription at a time, this can be seen clearly in the first example.
这对于像预先输入这样的场景非常有效,当新输入到达时,您不再关心先前请求的响应。 在长期存在的内部 observable 可能导致内存泄漏的情况下,这也是一个安全的选择。
Be careful though, you probably want to avoid switchMap in scenarios where every request needs to complete, think writes to a database. switchMap could cancel a request if the source emits quickly enough. In these scenarios mergeMap is the correct option.
但是要小心,您可能希望在每个请求都需要完成的情况下避免使用 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.
Creates an Observable that emits no items to the Observer and immediately emits a complete notification.
Just emits 'complete', and nothing else.
This static operator is useful for creating a simple Observable that only emits the complete notification. It can be used for composing with other Observables, such as in a mergeMap.
单步调试点了 pause 之后:
第 41 行 unsubscribe 方法,体现了 switchMap 自动取消先前的内部 observable 的特性:
先 cancel,再订阅新的 Observable:
如果返回 empty,就不会再执行后续的 scan 了:
可以把 switchMap 输入参数,即 projection 进行改造:
该 projection 的类型是一个箭头函数,输入参数是一个 boolean,输出是一个新的 Observable.
更多Jerry的原创文章,尽在:"汪子熙":