👨💻 介绍
对于 RxJS,多播
可能是一个比较高级的概念。
这篇文章会通过一个例子,逐步的介绍多播以及多播的操作符。
🔥 Hot & cold Observables
默认情况下,大多数 Observables
都属于 Cold Observables
,每次我们订阅 Cold Observable
时,都会重新创建它的生产者。那这是什么意思?
首先,我们必须知道生产者是什么:它可以是我们 Observable
值的来源,它页可以是 DOM 事件、回调、HTTP 请求、迭代器等,总之,任何可以产生价值并将其传递给观察者的东西。
现在我们知道了生产者是什么,就更容易理解前面语句的含义了,它基本上是说我们的 Observable 的生产者在每次订阅时都会被一遍又一遍地创建。让我们看一个例子:
import { timer } from "rxjs";
import { tap } from "rxjs/operators";
const cold$ = timer(1000).pipe(tap(() => console.log("副作用")));
cold$.subscribe(val => console.log(`Observer 1: ${val}`));
cold$.subscribe(val => console.log(`Observer 2: ${val}`));
// 自这个 Cold Observable 启动,副作用就执行两次,一次是给 subscription
/* Output:
副作用,
Observer 1: 0,
副作用,
Observer 2: 0
*/
如你所见,因为我们的 Observable
是 Cold Observable
,并且它的生产者在每次订阅时都被重新创建,所以每次订阅就执行一次,副作用总共执行了两次。
如果 Observable
是热的,不管我们订阅了多少次,副作用将只执行一次。
举个例子来说,假设我们有一个 Ajax Observable
,它可以获取一些数据。因为 Ajax Observable
是 Cold Observable
,每次订阅它时,都会发出一个新的 HTTP 请求。
20 个订阅 = 20 个 HTTP 请求。比如下面这样:
import { ajax } from "rxjs/ajax";
import { map, mergeAll, take, tap } from "rxjs/operators";
// 一个 Ajax(cold Observable)
const sharedGhibliFilm$ = ajax.getJSON("https://ghibliapi.herokuapp.com/films").pipe(tap(() => console.log("我请求啦")),mergeAll(),take(1));
// 两次 subscribe
sharedGhibliFilm$.pipe(map(({ title }) => title)).subscribe(title => console.log(`标题: ${title}`));
sharedGhibliFilm$.pipe(map(({ description }) => description)).subscribe(description => console.log(`描述: ${description}`));
// ... 两次请求
/* Output:
'我请求啦',
'标题: Castle in the Sky',
'我请求啦',
'描述: The orphan Sheeta inherited a mysterious crystal that...'
*/
如何才可以正确处理 热/冷 Observables 就变得很重要。
我们不想每次订阅都重新执行一遍,那就得将这个 Cold Observables
变成 Hot Observables
。使用 RxJS 中的多播就可以让这件事变得很方便。
☎️ 多播 (Multicast)
多播
通过使用 Subject 共享源Observable
。让我们看一个使用多播的例子:
import { interval, Subject } from "rxjs";
import { take, tap, multicast } from "rxjs/operators";
const number$ = interval(1000).pipe(take(10));
const multicast$ = number$.pipe(tap(() => console.log("执行了")),multicast(() => new Subject())
);
multicast$.subscribe(console.log);
// Output:
如果你将这段代码跑起来,你会发现它没有任何输出,这是为什么?
因为 multicast
返回一种特殊的 Observable
: ConnectableObservable
。
这种特殊类型的 Observable
有一个connect()
方法,当被调用时,它负责使用我们提供的 Subject
订阅源 Observable
。
这意味着如果我们不调用connect()
,流永远不会被订阅,也不会