RxJS原来应该这样用!

引言

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

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. 但是再登录系统,就登不进去了。

也就是说,这个注销方法影响了后续的登录,当时就很懵圈,为什么呢?

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

原因分析

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

clipboard.png

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

let isLogin$ = new BehaviorSubject();

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

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

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

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,最近没时间以后再细看,希望它不要再让我失望。

你可能感兴趣的:(rxjs,angular,观察者模式)