最近帮潘佳琦解决了一个诡异的问题,而后忽然发现⾃己对观察者感到迷茫了。typescript
需求是⼀个注销按钮,若是是技术机构登录,就调用技术机构的注销⽅法,若是是器具用户登录,就调⽤器具⽤户的注销方法。固然,最优的解决⽅案并不是我下⽂所列的,既然功能不同,那就应该是两个对象。看来咱们的⾯向对象运用得还不不够灵活。网络
注销代码以下,只表达思想,别去深究具体的语法:架构
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
,表示是技术机构登陆,则调用技术机构的注销方法。框架
然而,诡异的事情发生了:微服务
也就是说,这个注销方法影响了后续的登陆,当时就很懵圈,为何呢?性能
后来多打了几条日志,才发现了问题所在:this
根据Spring
官方的Spring Security and Angular
所述,官方作法是用一个boolean
值来判断当前是否登陆,从而进行视图的展现。spa
潘老师在新系统中也是根据官方的推荐进行实现的:设计
let isLogin$ = new BehaviorSubject<boolean>(); login() { ...... this.isLogin$.next(true); } logout() { ...... this.isLogin$.next(false); }
一个可观察的boolean
对象来判断当前用户是否登陆,而后main
组件订阅这个值,根据是否登陆并结合ngIf
来判断当前应该显示登陆组件仍是应用组件。3d
看这个图你们应该就明白了,问题出在了对subscribe
的理解上。
点击注销,发起订阅,当isLogin$
为true
的时候就注销,注销成功。
下次登陆时,这个订阅还在呢!而后点击登陆,执行登陆逻辑,将isLogin$
设置为true
,观察者就通知了订阅者,又执行了一遍注销逻辑。确定登不上去了。
执行订阅以后,应该获取返回值,再执行取消订阅。
因此,这个问题出在本该订阅一次,可是subscribe
是只要订阅过了,在取消订阅以前,我一直是这个可观察对象的观察者。
想到这我就迷茫了,就是我一订阅,一直到我取消订阅以前,这个可观察对象都要维护着观察者的列表。
那咱们的网络请求用的不也是Observable
吗?只是此Observable
是由HttpClient
构建好返回给咱们的。那咱们订阅了也没取消,那它是否是一直维护着这个关系,会不会有性能问题?难道咱们以前的用法都错了吗?
这个问题一直困扰了我好多天,知道今天才在Angular
官网上看到相关的介绍,才解决了个人迷茫。
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 done
,HttpClient
返回的Observable
对象只执行一次,而后就销毁。也就是所说的执行一次,或者是next
或者是error
,而后执行complete
销毁这个对象。
Angular
应该早就想到了维护观察者带来的性能问题,才设计一次性的观察者对象,我却是杞人忧天了。
RxJS
需求不一样,有时咱们须要一直订阅,有时咱们却想只订阅一次,不然就会发生一些很诡异的问题。
可是Observable
只提供了一个subscribe
方法,想订阅一次得手动取消。我想若是Observable
中再添加一个subscribeOnce
的方法,那开发起来会不会比如今更顺手?
Google
了一下,真的有啊!StackOverflow
上这老哥和我同样的想法,有没有相似subscribeOnce
这样式的方法?
回答得很棒,旧版本流式调用使用first
方法,新版本使用pipe
,里面再调用first
方法。
同时这里说了,若是first
的条件不符合时,会自动取消订阅。
Angular
中取消订阅关于如何在Angular
中最优雅地实现取消订阅,请参考这个问题:Angular/RxJs When should I unsubscribe from Subscription - StackOverflow
八百多个赞,回答得很是好,惋惜我看得是一脸懵逼,可能我开发经验不够。您感兴趣能够去看看原回答的实现方式。
若是您有任何的意见或建议,欢迎批评指正。
世上最难的不是学一门技术,而是如何实践。
没有教科书,没有项目参考,全靠一份官方文档,每一个人都创造着本身的最佳实践。
一路走来,从华软开始,也得四个月了,到如今也没设计出一款满意的前台架构。
优秀架构师的设计经验,确定不会分享给你,而普通的Angular
书籍,不过是一个小的Demo
项目,彻底没有考虑过项目很庞大的时候应该怎么组织架构。
最近发现了基于Angular
的前台微服务框架Mooa
,最近没时间之后再细看,但愿它不要再让我失望。