Angular中深度集成了Rxjs,只要你使用Angular框架,你就不可避免的会接触到RxJs相关的知识。

在Android开发中,绝大多数的Android开发者都用过RxJavaRxAndroidRxKotlin相关库。因其强大的操作符 以及 方便的线程切换 给我们日常开发提供了极大的便利。

但是,可能是前端并不像强类型语言那么严格,即使代码写的有点小问题,也是能照样运行,不仔细排查也发现不了什么影响。
在最近接触的Angular项目中,发现前端的小伙伴们很少去了解RxJs的原理,导致写的项目中,出现了很多只订阅不取消代码,以及 ObservableSubject 区别是什么都分不清的情况,导致项目运行时,一个事件发射出去,结果一堆订阅接收到了,甚至多次运行后,会出现重复执行几十次上百次的情况。

之前写过一篇在Android中如何优雅的处理自定取消订阅的方法,感兴趣的可以看下: AutoDispose代替RxLifecycle优雅的解决RxJava内存泄漏问题

本篇博客我们来看一下在Angular中如何优雅的处理RxJs自动取消订阅的问题。

方式一 (不推荐)

首先是常规用法,

我们在使用 subscribe 的时候会返回一个Subscription 对象,如下

该对象提供一个 unsubscribe() 方法,如下

我们只需要调用该方法即可取消订阅,一般都是在OnDestory中取消订阅。大致代码如下

  ngOnDestroy(): void {this.subject.unsubscribe()}

有时候我们不能仅限于会用,应该要知道为什么这样写。
Subject为例,我们可以大致瞧一眼,unsubscribe() 代码都写了些什么,为什么调用这个方法后就不会收到next() 发射的数据流了。

首先看下unsubscribe()

再看一下 next() 方法

代码一目了然,并没有什么高大上的代码,其实就是unSubscribe的时候将 closedisStopped 置为true, 在 next 的时候判断一下,如果已经close或者stop的话,就不发射数据了。

好了,扯远了,回归主题,为什么不建议这么写。

因为当你一个类中的 Subscription 特别多的时候,那么这种写法就很麻烦,每个都要 unsubscribe() 一遍,显然是不合适的。

方式二

使用takeUntil 操作符代替unsubscribe
我们先来看一下 takeUntil 操作符是干嘛的,以下是复制的源码及注释。

/*** Emits the values emitted by the source Observable until a `notifier`* Observable emits a value.** <span class="informal">Lets values pass until a second Observable,* `notifier`, emits a value. Then, it completes.</span>** ![](takeUntil.png)** `takeUntil` subscribes and begins mirroring the source Observable. It also* monitors a second Observable, `notifier` that you provide. If the `notifier`* emits a value, the output Observable stops mirroring the source Observable* and completes. If the `notifier` doesn't emit any value and completes* then `takeUntil` will pass all values.** ## Example* Tick every second until the first click happens* ```ts* import { fromEvent, interval } from 'rxjs';* import { takeUntil } from 'rxjs/operators';** const source = interval(1000);* const clicks = fromEvent(document, 'click');* const result = source.pipe(takeUntil(clicks));* result.subscribe(x => console.log(x));* ```** @see {@link take}* @see {@link takeLast}* @see {@link takeWhile}* @see {@link skip}** @param {Observable} notifier The Observable whose first emitted value will* cause the output Observable of `takeUntil` to stop emitting values from the* source Observable.* @return {Observable<T>} An Observable that emits the values from the source* Observable until such time as `notifier` emits its first value.* @method takeUntil* @owner Observable*/
export function takeUntil<T>(notifier: Observable<any>): MonoTypeOperatorFunction<T> {return (source: Observable<T>) => source.lift(new TakeUntilOperator(notifier));
}

注释的大概意思就是,当takeUntil里面的notifier接收到值时,就会终止数据流。这样一来,就达到了我们的目的。

大致用法如下:

 import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {private subject = new Subject();private $destory = new Subject<boolean>();constructor() {}/*** 组件初始化完毕时调用*/ngOnInit(): void {this.subject.pipe(takeUntil(this.$destory)//需要注意takeUntil要放在最后).subscribe(data => {console.log('data:', data);});}ngOnDestroy(): void {//this.subject.unsubscribe()this.$destory.next(true);this.$destory.unsubscribe();}}

这样一来即使一个组件中有很多订阅,也不用在 ngOnDestroy() 中写很多unSubscribe。

但是这样还是有些麻烦,毕竟每个组件都要写

 private $destory = new Subject<boolean>();ngOnDestroy(): void {//this.subject.unsubscribe()this.$destory.next(true);this.$destory.unsubscribe();}

这种重复代码。下面我们基于方式二稍微优化一下。

方式二优化(推荐)

我们可以写一个BaseComponent,将一些使用率很高的模板代码放到BaseComponent中,其他组件继承该组件即可。

例如:

BaseComponent

import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subject} from 'rxjs';@Component({selector: 'app-base',templateUrl: './base.component.html',styleUrls: ['./base.component.css']
})
export class BaseComponent implements OnInit, OnDestroy {$destory: Subject<boolean> = new Subject<boolean>();constructor() {}ngOnInit(): void {}ngOnDestroy(): void {this.$destory.next(true);this.$destory.unsubscribe();}}

让我们的组件继承BaseComponent,然后删除无用的模板代码

import {Component} from '@angular/core';
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {BaseComponent} from './base/base.component';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent extends BaseComponent {private subject = new Subject();constructor() {super();//继承后要加上super()}/*** 组件初始化完毕时调用*/ngOnInit(): void {this.subject.pipe(takeUntil(this.$destory)//需要注意takeUntil要放在最后).subscribe(data => {console.log('data:', data);});}}

其他组件同样继承BaseComponent即可。

我们还可以添加很多模板代码放到BaseComponent中,这样一来就可以减少很多无用的代码了,而且维护起来更加容易。


如果你觉得本文对你有帮助,麻烦动动手指顶一下,可以帮助到更多的开发者,如果文中有什么错误的地方,还望指正,码字不易,转载请注明转自喻志强的博客 ,谢谢!

Angular中优雅的处理RxJs自动取消订阅的方式以免出现内存泄露以及多次调用的问题相关推荐

  1. Angular自动取消订阅RxJs

    Angular自动取消订阅RxJs 在使用 rxjs 时我们经常忘记调用unsubscribe()而导致内存泄露,很多时候你很难发现它,在RxJs官方有这样一段话: What is a Subscri ...

  2. angular Observable 怎么自动取消订阅

    rxjs 的 Observable(可观察对象)极大的方便了我们的开发,但是当 subscribe(订阅) 没有多次时,前一个订阅没有取消,导致订阅方法被执行了多次. ngOnInit(): void ...

  3. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  4. java中的 dispose_RxJava2 中多种取消订阅 dispose 的方法梳理( 源码分析 )

    Github 相关代码: Github地址 一直感觉 RxJava2 的取消订阅有点混乱, 这样也能取消, 那样也能取消, 没能系统起来的感觉就像掉进了盘丝洞, 迷乱- 下面说说这几种情况 几种取消的 ...

  5. java dispose事件_Android-在 ViewModel 中使用 AutoDispose2 解决 RxJava 的内存泄露问题

    RxJava 的内存泄露问题 runRxLambda 和 runRxLambdaViewModel 只是个扩展函数,可以不用理会,下面两段代码唯一的区别就是第二段增加了 AndroidLifecycl ...

  6. 苹果系统这么没有关闭订阅服务器,iPhone 上没有取消订阅的选项怎么办?

    如果您在 iPhone 上订阅了应用提供的服务或内容,在取消订阅之前,大部分应用会自动续订.为了避免不必要的付费,用户可以前往 iPhone 设置-Apple ID-订阅中查看所有订阅的内容,并在此取 ...

  7. 这样,你才能算会 RxJava、RxAndroid 之取消订阅

    一:概述 前几天app 总是空指针奔溃,发现为某个 Fragment 中的控件为null 导致的.而且这个Fragment 是能够正常显示的.那么为什么还会空指针呢? 二:问题排查 这个app使用了 ...

  8. 【响应式编程的思维艺术】 (5)Angular中Rxjs的应用示例

    [摘要] Rxjs在angular中的基本应用 本文是[Rxjs 响应式编程-第四章 构建完整的Web应用程序]这篇文章的学习笔记. 示例代码托管在:http://www.github.com/das ...

  9. Vmare horizon client 5.0安装过程中自动取消

    Vmare horizon client 5.0安装过程中自动取消 从vmare官网下载的vmare horizon client 5.0安装过程中出现自动取消安装.希望本篇文档可能会对您有所帮助,但 ...

最新文章

  1. GitHub 大热!也许会成为你心中的OCR开源工具NO1!
  2. 网络编程学习笔记(tcp_connect函数)
  3. alter system|session set events转储
  4. 高可用性(HA),会话复制,多VM Payara群集
  5. java kryo register_java相关:Kryo框架使用方法代码示例
  6. win8 软件字体乱码
  7. 《深入浅出DPDK》读书笔记(四):并行计算-SIMD是Single-Instruction Multiple-Data(单指令多数据)
  8. java 多线程数据分发_使用Java多线程实现任务分发
  9. mysql处理varchar类型的between和and的时间问题少一天解决;
  10. 3说明书_怎么才能做好产品说明书翻译?知行翻译公司总结了3点
  11. 比较默认对象和默认约束的异同_UE4对象类类型引用和类默认对象(Class Default Object,简称CDO)...
  12. python安装whl_1 Matplotlib在win10下安装
  13. 宏定义(#ifndef+#define+#endif)的作用
  14. elementUI 下拉框隐藏时触发相关事件(下拉框下拉显示时不触发)
  15. Windows系统复制文件到虚拟机Linux环境的解决
  16. 汇编语言(一)-基础知识
  17. LVGL学习——初识动画 lv_anim_t
  18. 7种方式企业内部资料共享,你pick谁?
  19. 以太坊区块同步成功标志
  20. 倾斜补偿的电子罗盘(2):磁传感器的误差来源、硬磁干扰的校准(3个参数)、实验验证

热门文章

  1. MARKETS AND MARKET LOGIC——The Market‘s Principles (5)
  2. RFID珠宝防盗系统
  3. 在fluent计算时如何设置非稳态时的时间步长
  4. 计算机辅助绘图考试题,2017年CAD绘图员考试试题「附答案」
  5. Android实现VideoView重复播放本地视频(无缝播放)
  6. uniCloud + geoNear 地理位置查询
  7. jquery隐藏侧边栏和折叠侧边栏方法
  8. 传统场地内燃机车的电动化将是必然趋势
  9. OpenGov(一):什么是Polkadot Gov2
  10. 使用mac搭建vue脚手架项目