网址: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的原创文章,尽在:"汪子熙":

03-05 23:34