使用rxjs会经常用到取消订阅的操作,下面讲讲其取消订阅的几种方式
订阅返回对象
创建一个可观察对象,如:const observable = new Observable((subscriber) => {subscriber.next(1)}), 通过observable对象订阅返回可以订阅对象,每一次订阅返回一个对应的可订阅对象,所以可以通过可订阅对象取消对应订阅,如下
let subscriber
const observable = new Observable((subscriber) => {
subscriber.next(1);
subscriber = subscriber
})
// 订阅生成可订阅对象
const subscription$ = observable.subscribe(function subscribe(data) { console.log(data) })
// 取消订阅, subscribe函数不再执行,只会打印1,后面取消订阅了,2将不会打印
subscription$.unsubscribe()
subscriber.next(2)
这种方式当订阅对象多了,就很烦了,有多少个就要保存多少个变量,再一个个取消,但是肯定有存在的必要,当取消条件不同时比较合适,因条件不同所以必须保证订阅对象有区分,所以要用这样的方式
条件取消
条件取消使用takeUntil操作符,使用方法如下:
import { interval, fromEvent, takeUntil } from 'rxjs';
const source = interval(1000);
// 点击就会有流推出来
const clicks = fromEvent(document, 'click');
// takeUntil(clicks) 的意思是:clicks只要推出一个流,那么result订阅就会自动取消
const result = source.pipe(takeUntil(clicks)).subscribe(x => console.log(x));
这个常用套路就声明一个Subject对象,如下
const destroy$ = new Subject()
// 上面clicks换成 destroy$
// 再自己手动在要取消的时候调用 destroy$.next()
destroy$.next()
destroy$.complete() // 销掉Subject
看到这里感觉和上面的方式没什么区别,代码也没少什么,但这里可以集中取消,比如订阅了很多个,取消条件一样,那么最后取消时,只用在takeUntil中的对象推一次流就全部取消了,可以减少变量声明和成对的重复取消模板
多订阅场景,很多用的这种方式,也是比较推荐的方式,最常用的场景是在页面销毁时,执行上面最后两行代码,取消所有订阅
直接声明
直接声明Subscription类管理,直接上代码
import { interval, Subscription } from 'rxjs';
const subscription$ = new Subscription()
const source = interval(1000);
// 想要一起取消订阅对象可以加入里面
subscription$.add(source.subscribe(() => {console.log(1)}))
subscription$.add(source.subscribe(() => {console.log(2)}))
subscription$.add(source.subscribe(() => {console.log(3)}))
// 取消订阅,这里会取消上面的三个订阅
subscription$.unsubscribe()
这个和条件取消使用场景差不多,目前用的很少,没有takeUntil好用
好不好用,实践才知道
大多数场景是离开当前页面时取消相关订阅,所以多页面应用不用考虑(刷新页面没有了),单页应用spa,当离开当前页面时使用取消订阅,这里以vue和angular使用为例
vue3示例:
const source = interval(1000);
const sub$ = source.subscribe(() => {console.log(1)})
onUnmounted(() => {
sub$.unsubscribe()
})
改用use实现
function useSub(sub) {
onUnmounted(() => {
sub.unsubscribe()
})
}
// 那么就可以这样调用
useSub(source.subscribe(() => {console.log(1)}))
和vue无关写法, 结合响应式作用域来实现
// 在不清楚是否活跃时,可以用返回值判断,是否生效 这个在vue组件里用肯定活跃的,所以不用判断反回值
function useSub(sub) {
// 判断当前作用域是否活跃
if (getCurrentScope()) {
// 作用域不活跃时,取消订阅
onScopeDispose(() => {
sub.unsubscribe()
})
return true
}
return false
}
angular示例
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
})
export class AppComponent implements OnDestroy {
destroy$ = new Subject()
constructor() {
fromEvent(document, 'click').pipe(takeUntil(this.destroy$)).subscribe(() => {})
}
ngOnDestroy(): void {
this.destroy$.next()
this.destroy$.complete()
}
}
}
service 封装
// 服务
@Injectable()
export class DestroyService extends Subject implements OnDestroy {
constructor() { }
ngOnDestroy(): void {
this.next()
this.complete()
}
}
// 组件中使用
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less'],
providers: [DestroyService]
})
export class AppComponent {
constructor(private destroy$: DestroyService) {
fromEvent(document, 'click').pipe(takeUntil(this.destroy$)).subscribe(() => {})
}
}
个人觉得在 vue 和 angular 中使用rxjs取消订阅封装中,vue的封装和使用更简单,封装原理一样,订阅一个流,在不用时取消(组件销毁)
注意: 当一个流 报错(error) 或 完成了(complete)表示就已停止,这时订阅也就停了,所以这时的订阅可以不取消
以上如有不对的,欢迎留言交流学习