摘要:以传统方式来说,我们一般会搞一个store,上层业务的请求都是从它走,由它去拿到远程数据,并且控制缓存。但我们面临一个问题,当数据更新了,需要通知所有使用这个数据的业务方,所以就不得不在这里再补一种机制,用于通知上层。通常就是某种订阅机制:store1.subcribe("somedata", data => { //
以传统方式来说,我们一般会搞一个store,上层业务的请求都是从它走,由它去拿到远程数据,并且控制缓存。
但我们面临一个问题,当数据更新了,需要通知所有使用这个数据的业务方,所以就不得不在这里再补一种机制,用于通知上层。通常就是某种订阅机制:
store1.subcribe("somedata", data => { //处理这个data,更新到视图模型上供刷新界面 viewModel.somedata = data; });
这样问题是解决了,但业务代码就比较繁琐了,为什么我还要在每个地方都写订阅?尤其是如果上层用了angular或者vue,本来可以很简单的,你这一弄,框架刚帮我简化了一些代码,又被搞得很长了。
因为之前我们第一次查询数据,不是这么干的,而是:
service1.getData().then(data => { //处理这个data,更新到视图模型上供刷新界面 viewModel.somedata = data; });
所以这里就出现了不同的两种方式来更新界面数据,这非常讨厌。
我之前提出过一种折中的解决办法,比如在angular中,可以这样:
- store中保存数据对象的引用,上层业务在查询的时候,持有这个引用,并且,不得修改内容
- 如果需要修改或者更新远程数据,调用store中封装的修改方法,把数据合并到数据对象中
这样,界面上的刷新之类,可以不劳自己费心,但整体还是挺别扭的,而且,限制上层不能动引用,这个事情很坑,万一需要二次加工数据,就歇菜了。
React有一套机制来处理这个事情,留给这派的人来阐述吧。
我说说另外一种方式,FRP。
在egghead上有一个RxJS的视频系列,其中有一节大致介绍了使用Reactive理念处理这种场景,我觉得非常好,地址如下,可能要会员才能看:
Reactive Programming
它这个场景就是为了复用请求,里面的代码摘录:
var startupRequestStream = Rx.Observable.just('https://api.github.com/users');var requestOnRefreshStream = refreshClickStream .map(ev => { var randomOffset = Math.floor(Math.random()*500); return 'https://api.github.com/users?since=' + randomOffset; });var requestStream = startupRequestStream.merge(requestOnRefreshStream);var responseStream = requestStream .flatMap(requestUrl => Rx.Observable.fromPromise(jQuery.getJSON(requestUrl)) ) .shareReplay(1); // refreshClickStream: -------f-------------> // requestStream: r------r-------------> // responseStream: ---R-------R---------> // closeClickStream: ---------------x-----> // suggestion1Stream: N--u---N---u---u-----> function getRandomUser(listUsers) { return listUsers[Math.floor(Math.random()*listUsers.length)];}function createSuggestionStream(responseStream, closeClickStream) { return responseStream.map(getRandomUser) .startWith(null) .merge(refreshClickStream.map(ev => null)) .merge( closeClickStream.withLatestFrom(responseStream, (x, R) => getRandomUser(R)) );}
这里面的关键在于:shareReplay和withLatestFrom。
shareReplay的介绍在这:shareReplay | RxJS
withLatestFrom的介绍在这:withLatestFrom
抛开代码细节不谈,我觉得理念是这样:
我把请求当做流,然后,上层业务来订阅这个流,正常来说,我们的期望可能是,订阅之后,流的每次更新都会得到处理,但这时可能出现:
某一个订阅者来得太晚,流已经走了一段数据了,会需要“补课”。
所以,Rx的机制会允许我们缓存一些数据,供“我们来晚了”的朋友们使用。
使用这种流的理念来处理,有什么好处呢?那就是可以保证上层业务的一致性。对于上层代码,我只管是从这个流获取数据,更新界面,我不关注你是哪来的,只认你一家,反正只要根据你那边的信息,都作了对应处理,就肯定好了。
而这个流的提供方,也很方便,他只需考虑把不同来源的数据进行归并,从多个流合并成一个流,就是这么简单。
Angular 2深度整合了RxJS,可以关注,不过RxJS本身是不绑定到任何框架的,你跟jQuery或者React配合使用,也是没问题。