响应式编程101

来源:互联网 发布:知乎成都工作 编辑:程序博客网 时间:2024/06/07 21:45

Reactive Programming 101

Orginal from : The introduction to Reactive Programming you’ve been missing

Reactive Programming 响应式编程

Reactive programming is programming with asynchronous data streams.
响应式编程是异步数据流
这不是什么新概念,一个点击事件都是一个异步事件流,你可以观察并且做一些副作用。
数据流可以在任何地方,变量,用户输入,属性,caches,数据结构。
在往上,你可以对流做一些更改,合流,截流,改道,筛选。combine, create and filter。

一个流是一些列随时间正在进行的事件。会发出3种信号,值,错误,完成。
这里写图片描述
这些发出的事件被我们异步地捕捉,定义3个触发函数分别对应3个信号(值,错误,完成)。返回对应的函数时触发对应的参数。
有时候我们只需要注意返回信号的触发函数,而不需要其他信号的触发函数。
监听流的行为叫做订购(subscribing)。监听函数定义为观察者(observers).
流被观察,叫做观察者设计模式(Observer Design Pattern)。

举例做一个记录点击数的按钮,在大部分响应式库里,流通常绑定函数。
当触发这些函数时,会返回一个新的流。流和新流之间互不干扰。
这里的流是clickStream,新流是counterStream。

clickStream: ---c----c--c----c------c-->               vvvvv map(c becomes 1) vvvv               ---1----1--1----1------1-->               vvvvvvvvv scan(+) vvvvvvvvvcounterStream: ---1----2--3----4------5-->

触发map(f)函数(返回新流)时,会替换对应值,这里替换c为1。
scan(g)函数会相加之前的值在这个流上(map函数触发的新流),
最终两个函数触发完以后,返回counterStream流。

为了体现响应式编程的优点,创建一个双击事件流,这个流会把大于2次以上的点击全部计算成双击。
原理如下
这里写图片描述
灰色区域就是对流的修改,也就是3个函数。
先对击数做提取,假设以250mm为界限,提取出对应的击数。
然后把击数替换成数值
最后做一个filter,把>2的筛选出来。

Example 实际案例

选用JS和RxJS作为工具。

以Twitter推荐follow为例,这个部分建议要follow的人。
这里写图片描述
重点分析:
- 在启动时,从api中读取用户信息并显示3个建议
- 刷新时,加载其他3个建议
- 点击x关闭时,对应建议消失,并出现新的建议
- 每一行显示3个建议的信息和其连接

在启动时,从api中读取用户信息并显示3个建议
简化成 1)request 2)get responce 3)render responce
把请求想象成流,一开始是这样

--a------|->a 就是api请求 `'https://api.github.com/users'`

当一个请求事件发生时,有两个信息,when and what,
何时该执行 就是请求发生时的时间,
what 就是请求内容,这里是一串url。

在Rx*里面,创建一个流的官方名字是观察者,但是直接叫流(stream)更合适。

var requestStream = Rx.Observable.just('https://api.github.com/users');

订阅流,以便操作,然后用Ajax回调函数,处理这个异步请求。

requestStream.subscribe(function(requestUrl) {  // execute the request  var responseStream = Rx.Observable.create(function (observer) {    jQuery.getJSON(requestUrl)    .done(function(response) { observer.onNext(response); })    .fail(function(jqXHR, status, error) { observer.onError(error); })    .always(function() { observer.onCompleted(); });  });  responseStream.subscribe(function(response) {    // do something with the response  });}

Rx.Observable.create()用于创建专属定制流,通过把数据事件传通告给各个观察者,或者订阅者。
Promise 也是一个观察者,只不过接收一个参数。
Rx流却可以处理很多。

map(f) 从流a提取数据,实施函数f,然后产生流b。会产生流中流,分流。
本例中,返回流如果用map,会产生新的流(流走了,收不到?)。同时请求和返回都是以promise单独存在。

这里写图片描述
我们需要让map不产生新的流,以便能够接受。
所以用flatMap,扁平版map,不会产生流b
这里写图片描述

requestStream:  --a-----b--c------------|->responseStream: -----A--------B-----C---|->

收到response流后,就可以渲染了。

刷新

做一个刷新点击流(刷新按钮),同时请求流需要依靠刷新点击流。
RxJS内置对应工具

var refreshButton = document.querySelector('.refresh');var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');

刷新事件流本身不带有api url,需要给予对url通过map。
把请求流连接刷新流

var requestOnRefreshStream = refreshClickStream  .map(function() {    var randomOffset = Math.floor(Math.random()*500);    return 'https://api.github.com/users?since=' + randomOffset;  });var startupRequestStream = Rx.Observable.just('https://api.github.com/users');

合并两个流

stream A: ---a--------e-----o----->stream B: -----B---C-----D-------->          vvvvvvvvv merge vvvvvvvvv          ---a-B---C--e--D--o----->

modeling 3 suggestion

每一个建议当成流,

整体流程如下

refreshClickStream: ----------o--------o---->     requestStream: -r--------r--------r---->    responseStream: ----R---------R------R-->    suggestion1Stream: ----s-----N---s----N-s--> suggestion2Stream: ----q-----N---q----N-q--> suggestion3Stream: ----t-----N---t----N-t-->

n为null
或者设置在启动时数据默认为n

refreshClickStream: ----------o---------o---->     requestStream: -r--------r---------r---->    responseStream: ----R----------R------R-->    suggestion1Stream: -N--s-----N----s----N-s--> suggestion2Stream: -N--q-----N----q----N-q--> suggestion3Stream: -N--t-----N----t----N-t-->

关闭 出现

关闭一个建议,加载一个新的。

requestStream: --r--------------->   responseStream: ------R----------->close1ClickStream: ------------c----->suggestion1Stream: ------s-----s----->

Rx*有函数combineLatest可以绑定两个流最近的 如图:

stream A: --a-----------e--------i-------->stream B: -----b----c--------d-------q---->          vvvvvvvv combineLatest(f) vvvvvvv          ----AB---AC--EC---ED--ID--IQ---->

可以用来绑定close1ClickStreamsuggestion1Stream