RxJS 系列 – Observable to Subject (Hot, Cold, Warm, connectable, share)
前言
前两篇介绍了 Observable 和 Subject.它们有一个重大区别当 multiple subscribe 的时候.
Observable 每一次 subscribe 都会调用初始化方法, 并且创建出独立的一个 stream.
Subject 则只是把 subscriber 存起来, next 的时候批量调用.
许多人也把 Observable 这种每一次都会创建出新的 source 的行为称为 Cold Observable
而向 Subject 那样每次 subscribe 不会创建出新 source 的行为称为 Hot Observable
参考: Medium – Hot vs Cold Observables
通常我们比较喜欢 Hot Observable (但不是 100%) 所以 RxJS 有一些方法可以把 Cold 变成 Hot.
这篇主要就是介绍这个.
参考
Cold (a.k.a Unicast) Observable Behaviour
const obs = new Observable(subscriber => {
console.log('Observable init');
let index = 0;
const intervalNumber = setInterval(() => {
subscriber.next(index++);
if (index === 5) {
clearInterval(intervalNumber);
subscriber.complete();
}
}, 1000);
return () => {
console.log('Displose Observable');
clearInterval(intervalNumber);
};
});
obs.subscribe({
next: v => console.log('first', v),
complete: () => console.log('complete'),
});
setTimeout(() => {
obs.subscribe({
next: v => console.log('second', v),
complete: () => console.log('complete'),
});
}, 2000);
有一个 Observable, 被 subscribe 2 次
效果

它会创建出 2 个独立的 interval, 相互不影响.
Cold to Hot (a.k.a Multicast) 原理
如果我们希望它只创建一次 interval, 2 个 subscribe 订阅同一个 source 可以吗?
可以, 它的底层实现原理就搞一个中间人 Subject.
Subject 可以 multiple subscribe 同一个 source.

这样 Observable 只被 subscribe 一次所以只会有一个 interval, 而 Subject 可以被 subscribe 多次.
RxJS 6.0 和 7.0 的区别
RxJS 6.0 和 7.0 Cold to Hot 的方法差距甚远. 这里只介绍 7.0 的方案, 如果想考古可以参考
RxJS Multicast 類 Operator (1) - multicast / publish / refCount / share / shareReplay
30 天精通 RxJS(24): Observable operators - multicast, refCount, publish, share
The magic of RXJS sharing operators and their differences

connectable
create connectable. connect and disconnect
RxJS 提供了一些方法, 方便我们实现 Cold to Hot, 原理就像上面说的.
const con = connectable(obs, {
connector: () => {
console.log('create connector');
return new Subject();
},
resetOnDisconnect: false,
});
const subscription = con.connect();
subscription.unsubscribe();
con.connect();
connectable 会返回一个 Connectable 对象. 当调用 .connect() 的时候.
observable 被订阅. 当 .unsubscribe 的时候, obserable 被退订 (displose)
resetOnDisconnect 是声明当 re-connect (unsubscribe 之后又 connect) 是否创建新的 Subject 还是复用之前的, 默认是 true.
像上面这个例子, 当第二次调用 .connect 时, 'create connector' 不会触发 (它只会触发一次), 而如果 resetOnDisconnect: true 那么它就会触发 2 次.
为什么要搞 reset 呢? 因为 Subject 有可能 completed, 是否要保留之前的记入要依据项目需求.
subscribe connectable
con.subscribe({
next: v => console.log('first', v),
complete: () => console.log('complete'),
});
setTimeout(() => {
con.subscribe({
next: v => console.log('second', v),
complete: () => console.log('complete'),
});
}, 2000);
接着其它的 subscirbe 都 apply 到 connectable 上, 它就是中间人.
效果

2 个 subscribe 都 apply 到了同一个 source 上.
Cold to Hold 要注意的事项
Observable 的特色是, subscribe 时才初始化, unsubscribe 以后 displose.
但一旦 Subject 介入以后, 什么时候初始化, 什么时候 displose 就变成一个要思考的问题了.
大部分情况, 我们会认为, 当第一个 subscribe 调用时, Observable 才被初始化. (不是 100% 场景都这样啦)
上面 connectable 的例子中, 我是一早就调用了 connect, 这样 Observable 立马就初始化了. 而不是等到第一次被 subscribe. 所以不符合现在的要求.
另外, 大部分情况下, 我们会认为, 当所有的 subscriber unsubscribe 以后, Observable 被 displose (不是 100% 场景都这样啦)
上面 connectable 的例子中, Observable 最终会 complete, 所以它不是通过 unsubscribe 形成 displose 的.
虽然例子没有符合大部分的场景, 但不要紧, connectable 的接口足够底层, 我们完全可以控制什么时候要 connect 什么时候要 disconnect (displose)
但由于大部分场景真的就是那样, 所以 RxJS 封装了一个 share 方法, 让我们更容易去实现这种 connect 和 disconnect 的逻辑.
Share
share 和 connectable 差不多, 只是它不需要我们去管理 connect 和 disconnect.
const shareableObs = obs.pipe(
share({
connector: () => {
console.log('create connector');
return new Subject();
},
resetOnComplete: true,
resetOnError: true,
resetOnRefCountZero: true,
})
);
它的规则是这样的, 当第一个 subscribe 调用后, Observable 被订阅. 接着的 subscribe 不会再初始化 Observable
当 Observable complete 或者 error 以后. 如果有新的 subscriber, 那么 Observable 会被重新初始化.
这就是 resetOnComplete 和 resetOnError 的意思. 如果 false 就表示不会重新初始化, 那么这个 subscriber 会直接收到 complete 或者 error.
resetOnRefCountZero 指的是, 当所有 subscriber unsubscribe 以后 (ref count = zero), 是否要退订 Observable. true 表示要. 那么 Observable 就 displose 了
当下一个 subscribe 来后, Observable 重新初始化. 而 false 表示不要退订, 那么 Observable 只能靠自己 complete 或 error 才会 displose.
默认 3 个都是 true 哦。

题外话, 过往的经验
以前写 Angular 的时候, 遇过这样一个情况, component 和 view 分别需要 subscribe 一个 stream.
component 只要 take 1, 然后我配上 await toPromise. 这样 ref count 就变成 0 了. 当 view 渲染的时候 Observable 又初始化了.
可如果设置 resetOnRefCountZero: false 又可能导致 Observable 无法 displose.
目前看解决思路有 2 个.
1. 搞一个假的 subscriber hacking 它. 让它的 ref count 不会变成 0 直到 component destroy.
2. 使用 connectable 自己管理 connect 和 disconnect.
ShareReplay
它底层调用的是 share, 只是用了 ReplaySubject. 这样可以 cache value.

注意:它的 resetOnComplete 和 refCount 默认是 false,这和 share 的默认不同。
提醒:refCount false 就代表永远不会 unsubscribe 了,如果我们想自己控制何时 unsubscribe 就需要用比较底层的 share。
const obs = new Observable(subscriber => {
console.log('init observable');
return () => console.log('displose observable');
});
const destroySubject = new Subject<void>();
const sharedObs = obs.pipe(
share({
connector: () => {
console.log('create connector');
return new ReplaySubject(1);
},
resetOnComplete: true,
resetOnError: true,
resetOnRefCountZero: () => destroySubject, // 依据 destroy subject 决定什么时候 unsubscribe source observable
}),
);
const sub1 = sharedObs.subscribe();
const sub2 = sharedObs.subscribe();
sub1.unsubscribe();
sub2.unsubscribe(); // 0 subscriber 不会导致 displose observable
destroySubject.next(); // destroy 发布就会
总结
Observable 遇到 multiple subscribe, 会创建出多个独立的 stream. 但有时我们是想共享 stream 的.
这时就可以用 connectable 或者 share 来达到目的.
其原理是在中间加了一层 Subject. 由 Subject 来 subscribe Observable, 其它人 subscribe Subject.
此外我们需要注意 Observable 的初始化和 displose 的时机.
RxJS 系列 – Observable to Subject (Hot, Cold, Warm, connectable, share)的更多相关文章
- Angular学习笔记—RxJS与Observable(转载)
1. Observable与观察者模式的关系 其实这里讲的Observable就是一种观察者模式,只不过RxJS把Observable结合了迭代模式以及附件了很多的operator,让他变得很强大,也 ...
- [RxJS] Reusable multicasting with Subject factories
The way we use publish() (or multicast with an RxJS Subject) makes the shared Observable not reusabl ...
- [RxJS] Creating Observable From Scratch
Get a better understanding of the RxJS Observable by implementing one that's similar from the ground ...
- [RxJS] Using Observable.create for fine-grained control
Sometimes, the helper methods that RxJS ships with such as fromEvent, fromPromise etc don't always p ...
- ng-packagr 打包报错 Public property X of exported class has or is using name 'Observable' from external module “/rxjs/internal/Observable” but cannot be named
old import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable( ...
- Rxjs 修改Observable 里的值
有这么一个对象c$: Observable<any> 修改里边的值: 声明一个subject subject: Subject<any>; 在ngOnInit()中进行初始化 ...
- RxJS学习笔记之Subject
本文为原创文章,转载请标明出处 目录 Subject BehaviorSubject ReplaySubject AsyncSubject 1. Subject 总的来说,Subject 既是能够将值 ...
- shiro实战系列(十)之Subject
毫无疑问,在 Apache Shiro 中最重要的概念就是 Subject.'Subject'仅仅是一个安全术语,是指应用程序用户的特定 安全的“视图”.一个 Shiro Subject 实例代表了一 ...
- [RxJS] Hot Observable, by .share()
.share() is an alias for .publish().refCount(). So if the source is not yet completed, no matter how ...
- [RxJS] Subject: an Observable and Observer hybrid
This lesson teaches you how a Subject is simply a hybrid of Observable and Observer which can act as ...
随机推荐
- [MAUI 项目实战] 笔记App:程序设计
前言 有人说现在记事类app这么多,市场这么卷,为什么还想做一个笔记类App? 一来,去年小孩刚出生,需要一个可以记录喂奶时间的app,发现市面上没有一款app能够在两步内简单记录一个时间,可能iOS ...
- scratch少儿编程卡通三国背景72张全套素材包【免费下载】
scratch卡通三国题材背景图片,共72张,让你轻松打造scratch三国世界! 免费下载地址:https://www.xiaohujing.com.cn 这套背景图片以卡通风格呈现,色彩鲜艳.造型 ...
- hbuilderx生成ios证书和上架全教程
现在很多公司都使用uniapp作为底层框架来开发app应用,而uniapp的开发工具hbuilderx云打包的时候,需要证书和证书profile文件. 假如是ios应用,则还需要上架到appstore ...
- pytest + 登录禅道 :自动提交bug-编辑bug-确认bug-解决bug-关闭bug
pytest + 登录禅道 :自动提交bug-编辑bug-确认bug-解决bug-关闭bug 一.Pycharm中创建项目结构 1.新建一个工程,工程名称自己定义,如:zentao 2.在工程的根目 ...
- Fiddler使用界面介绍-右侧面板
右侧面板是对左侧请求进行解析的面板,点击左侧的请求右侧面板就会出现分析数据 1.Statistics关于HTTP请求的性能 2.Inspectors请求内容,包含请求数据和响应数据 3. AutoRe ...
- 【WEB】URL文件
早些年接触电脑的时候就有这个东西,去网站上下载盗版游戏,网站会附加这种URL文件 双击运行之后是打开浏览器跳转到该文件描述的网址 我从来没想过这东西里面写的是什么 百度经验: https://baij ...
- 为啥华为Atlas的AI卡在二手市场上特别多
首先阐述一下事实,那就是华为Atlas的AI卡在二手市场上特别多,基本上在某鱼上一搜索就是满屏,尤其是关键词:华为Atlas300,但是作为同等level的NVIDIA公司的A100却较之相比少的多, ...
- .NET电子邮件高效处理解决方案
前言 在日常软件开发中,电子邮件处理是一个不可或缺的功能,无论是用户注册验证.通知推送还是日常的业务沟通,都离不开电子邮件的支持.今天大姚给大家分享2款.NET开源.高效.强大的.NET电子邮件处理库 ...
- 祝贺小鹏汽车Gallardot同学成为Apache DolphinScheduler Committer!
社区迎来新committer!这次是来自小鹏汽车的Gallardot,看看他与Apache DolphinScheduler社区的故事吧. 对话社区 Q1:您为Apache DolphinSchedu ...
- 全网最适合入门的面向对象编程教程:35 Python的内置数据类型-文档字符串和__doc__属性
全网最适合入门的面向对象编程教程:35 Python 的内置数据类型-文档字符串和__doc__属性 摘要: 在 Python 中,文档字符串(Docstring)是一种用于为模块.类.方法或函数编写 ...