Rxjs实践技巧:从一个小案例说开去
接触Rxjs已经有一些时日了,只是一直没能真正实践过。最近工作上有个功能让我不得不对Rxjs进行更多深入的思考。其实Rxjs这个玩意儿从理论上就已经很难理解了,但是在实践中会发现更加棘手。
大致的思路很简单,我们需要获取每个selected macros的权限,然后根据”edit“和”delete“各自不同的逻辑去组合相应的权限,然后得出它们是否可用的逻辑值。这里有三个前提要注意:
我们先来说delete,回顾一下delete的业务逻辑:至少有一个macro的时候,并且所有的macro都有delete的权限才可用。我们大致有这样的思路: if (checkedItems.length > 0) { return true; } else { foreach(checkedItem of checkedItems) { permission = getPermissionFromAPI(checkedItem.Id); } do "&" with all permission; return result; } 我们获取的每一个macro权限都是一个流,得到了一个流的集合,然后还要把集合中每一个流里面的数据取出来进行&运算。每个权限都独自成流,然而我还要对这若干的流进行组合操作,很容易想到的就是mergy为一个流,我一开始的算法是这样的:把所有的权限流放在一个数组里,然后用Observable.from(arr)把它变成“流的流”,也就是发射的数据本身也是个流的流,然后用mergeAll把这个双重流中的数据合并到一起变成单个流。然后对单个的流取出权限,进行&操作。难题又来了:怎么把这些delete权限进行&运算得到一个是否可用的boolean值。我们是进行流操作的,当然不可能像同步式编程那样取出所有权限值进行链式的&(p1 & p2 & p3),那么什么样的操作符可以帮助我们完成类似的效果呢?其实我们发现这种链式的&操作其实就是一种“归并操作”,我们平日里总是以求和来举例“归并reduce”,其实这种逻辑的链式操作又何尝不是一种reduce呢。reduce的初始值为selecteditems.length>0,然后取出合并流中每个delete权限和初始值进行&操作,归并后的结果就是delete按钮是否可用的布尔值。 let permissions: Array<any> = []; foreach(checkedItem of checkedItems) { p$: Observable<any> = getDeletePermissionFromAPI(checkedItem.Id); permissions.push(p$); } Observable.from(permissions).mergyAll().reduce((isable: boolean,permission) => { return isable && permission; }); 用mergyAll是唯一的解决方案吗,有没有其他操作符也能解决问题呢?其实处理多个流合并的问题,还有zip,concatAll,combineLatest等等操作符可用。我这里并不focus在比较它们的异同点,只是举出还有别的可能性,比如以zip为例,我们可以得到第二种写法: let permissions: Array<any> = []; foreach(checkedItem of checkedItems) { p$: Observable<any> = getDeletePermissionFromAPI(checkedItem.Id); permissions.push(p$); } Observable.zip(...permissions).reduce((isable: boolean,permission) => { return isable && permission; }); 这里补充说明一下,由于zip的参数是若干个流,所以我们这里把流的数组permssions通过spread(...)来打散为元素,这里是一个技巧。 this.service.getCheckedItems$().map(checkedItems: any => { //上述delete逻辑,此处省略 ...... }) 我们上述的delete的逻辑,我们把checkeditems先变成了流的数组,然后通过一系列的操作符转换为了一个包含最后逻辑值的流。也就是说,外层流checkeditems的每一个值都映射为了一个包裹着逻辑值的新的流,这就是双层流的嵌套问题,disable属性的流每次发射的元素应该只是我们计算出的逻辑值,而不是一个新的流,那么怎么解决这个问题呢,switchMap是解决这种问题的利器。map是将流中的元素转化为另一个元素,switchMap是将一个流转化为另外一个流,由于我们产生了新的流,那么就用switchMap从主流转到从流上,伪代码如下: this.service.getCheckedItems$().switchMap(checkedItems: any => { //上述delete逻辑,此处省略 ...... }) 使用switchMap操作符把主流转化为从流 /** 一次性访问API获取所有macro的权限,并以键值对的方式存储在一个对象中 **/ let allPermissions = {}; foreach(macro in macros) { let p$ = this.service.getPermissionFromAPI(macro.Id); allPermissions[macro.Id] = p$; } this.service.getCheckedItem$.map(checkedItems => { return checkedItems.map((checkedItem) => { return allPermissions[checkedItem.Id] }) }).switchMap(permissions$ => { return Observable.zip(...permissions$).reduce((isable: boolean,permission) => { return isable && permission; },true); }) 至此,delete的所有问题都得到了比较完美的解答,其实edit的逻辑也是类似的,而且比delete更简单,可以留给读者自己思考怎么来写。最后我们总结一下,通过这个小案例我们有哪些收获:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |