RxJS原来应该这样用!

引言

最近帮潘佳琦解决了一个诡异的问题,而后忽然发现⾃己对观察者感到迷茫了。typescript

clipboard.png

需求是⼀个注销按钮,若是是技术机构登录,就调用技术机构的注销⽅法,若是是器具用户登录,就调⽤器具⽤户的注销方法。固然,最优的解决⽅案并不是我下⽂所列的,既然功能不同,那就应该是两个对象。看来咱们的⾯向对象运用得还不不够灵活。网络

原问题解决

问题描述

注销代码以下,只表达思想,别去深究具体的语法:架构

logout(): void {
    this.departmentService.isLogin$.subscribe((isDepartmentLogin) => {
        if (isDepartmentLogin) {
            this.departmentLogout();
        }
    });

    this.systemService.isLogin$.subscribe((isTechnicalLogin) => {
        if (isTechnicalLogin) {
            this.technicalLogout();
        }
    });
}

看着好像没啥错误啊?订阅获取当前登陆用户状态,若是departmentService.isLogin$true,表示是器具用户登陆,则调用器具用户的注销方法;若是是systemService.isLogin$true,表示是技术机构登陆,则调用技术机构的注销方法。框架

然而,诡异的事情发生了:微服务

  1. 首次打开系统,登陆,登陆成功。
  2. 点击注销,也能注销成功。
  3. 可是再登陆系统,就登不进去了。

也就是说,这个注销方法影响了后续的登陆,当时就很懵圈,为何呢?性能

后来多打了几条日志,才发现了问题所在:this

缘由分析

根据Spring官方的Spring Security and Angular所述,官方作法是用一个boolean值来判断当前是否登陆,从而进行视图的展现。spa

clipboard.png

潘老师在新系统中也是根据官方的推荐进行实现的:设计

let isLogin$ = new BehaviorSubject<boolean>();

login() {
    ......
    this.isLogin$.next(true);
}

logout() {
    ......
    this.isLogin$.next(false);
}

一个可观察的boolean对象来判断当前用户是否登陆,而后main组件订阅这个值,根据是否登陆并结合ngIf来判断当前应该显示登陆组件仍是应用组件。3d

clipboard.png

看这个图你们应该就明白了,问题出在了对subscribe的理解上。

点击注销,发起订阅,当isLogin$true的时候就注销,注销成功。

下次登陆时,这个订阅还在呢!而后点击登陆,执行登陆逻辑,将isLogin$设置为true,观察者就通知了订阅者,又执行了一遍注销逻辑。确定登不上去了。

执行订阅以后,应该获取返回值,再执行取消订阅。

迷茫

因此,这个问题出在本该订阅一次,可是subscribe是只要订阅过了,在取消订阅以前,我一直是这个可观察对象的观察者。

想到这我就迷茫了,就是我一订阅,一直到我取消订阅以前,这个可观察对象都要维护着观察者的列表。

那咱们的网络请求用的不也是Observable吗?只是此Observable是由HttpClient构建好返回给咱们的。那咱们订阅了也没取消,那它是否是一直维护着这个关系,会不会有性能问题?难道咱们以前的用法都错了吗?

这个问题一直困扰了我好多天,知道今天才在Angular官网上看到相关的介绍,才解决了个人迷茫。

clipboard.png

HttpClient.get()方法正常状况下只会返回一个可观察对象,它或者发出数据,或者发出错误。有些人说它是“一次性完成”的可观察对象。

The HttpClient.get() method normally returns an observable that either emits the data or an error. Some folks describe it as a "one and done" observable.

或许英文文档描述得更准确,one and doneHttpClient返回的Observable对象只执行一次,而后就销毁。也就是所说的执行一次,或者是next或者是error,而后执行complete销毁这个对象。

Angular应该早就想到了维护观察者带来的性能问题,才设计一次性的观察者对象,我却是杞人忧天了。

拓展RxJS

一次订阅解决方案

需求不一样,有时咱们须要一直订阅,有时咱们却想只订阅一次,不然就会发生一些很诡异的问题。

可是Observable只提供了一个subscribe方法,想订阅一次得手动取消。我想若是Observable中再添加一个subscribeOnce的方法,那开发起来会不会比如今更顺手?

clipboard.png

Google了一下,真的有啊!StackOverflow上这老哥和我同样的想法,有没有相似subscribeOnce这样式的方法?

clipboard.png

回答得很棒,旧版本流式调用使用first方法,新版本使用pipe,里面再调用first方法。

同时这里说了,若是first的条件不符合时,会自动取消订阅。

Angular中取消订阅

关于如何在Angular中最优雅地实现取消订阅,请参考这个问题:Angular/RxJs When should I unsubscribe from Subscription - StackOverflow

clipboard.png

八百多个赞,回答得很是好,惋惜我看得是一脸懵逼,可能我开发经验不够。您感兴趣能够去看看原回答的实现方式。

若是您有任何的意见或建议,欢迎批评指正。

总结

世上最难的不是学一门技术,而是如何实践。

没有教科书,没有项目参考,全靠一份官方文档,每一个人都创造着本身的最佳实践。

一路走来,从华软开始,也得四个月了,到如今也没设计出一款满意的前台架构。

优秀架构师的设计经验,确定不会分享给你,而普通的Angular书籍,不过是一个小的Demo项目,彻底没有考虑过项目很庞大的时候应该怎么组织架构。

最近发现了基于Angular的前台微服务框架Mooa,最近没时间之后再细看,但愿它不要再让我失望。

相关文章
相关标签/搜索