RxJS API解析(一)
起因
在SegmentFault里发布过一篇RxJS的简明教程,很多人反馈对这个主题很是很感兴趣,详见RxJS简明教程。
Rx 是一种编程的思维,而不是一个特定的框架或库。RxJS是Rx*基于Javascript语言栈的实现。
我决定,今后写一系列“深入浅出”的文章来介绍 Rx*。我选择RxJS作为base,所有的代码实例都会基于RxJS,这一系列文章主要会涉及以下几个方面:
我对Rx的理解,和使用中的感悟,不会拘泥于前端或是服务端。
对Rx*标准:对象、方法(API)的阐述,这部分相当于对API文档的翻译。
这个系列,坚持原创和对国外优秀材料的翻译。当然这是个浩大的工程,希望我可以坚持完成。
Rx* (Observable.amb & Observable#amb)
注:Object.method为对象方法,Object#method为实例方法
方法定义
[Rx.Observable.amb(...args)
]
作用
从一系列流中,订阅最先发射的值的可观察对象并忽略其他的可观察对象。
参数
args
(Array|arguments):方法参数为多个可观察对象(流),或者是Promise对象,对象间存在竞争关系。
返回值
(Observable
) :方法返回呈竞争态的多个可观察对象中,首先发射的可观察对象。
总结
简单的说,amb()
像一个多路电闸,一次仅能构建一条通路:
| | | | | | | | A B C D E F G H | | | | | | | | \ \ 开关臂 \ | 主线 |
函数需要做出 选择 ,选择的依据就是哪一个可观察对象(流)先发射了值。选择后,仅有“联通”的可观察对象会被观察到。还是用 电路 做比喻,其中“ * ”表示电子:
* | | | | | | | * | | | | | | | A B C D E F G | | | | | | | * | | | | | | | * | | | | | | | *
可以看到,E支流的电子先到达了末端,所以E路被接通。从外部看,所有订阅者仅能观测到这个联通了E支流。
Rx官方喜欢使用珠宝图来解释各个操作符(函数)的作用,珠宝图表示amb()
。
介绍一下牛逼的 珠宝图 :
从左到右的箭头,代表时间轴。|
代表可观察对象(流)发出了完成信号。
轴上的每一个珠宝代表流发射的值;
下方amd
那个层是处理操作符,本图意味着所有操作符以上的流,都会经过操作符的处理(操作符以上的流为操作符的操作数);
最下方,是操作符处理后的输出结果。y = f(x)
,其中x
表示输入流,f()
是操作符,y
是最后的输出流。
观察上面的珠宝图,1, 2, 3
这条时间轴上的可观察对象发射了值1
,所以amb()
选择了它作为最终输出的可观察对象。接下来如果它被订阅,订阅者会依次收到1
,2
和 3
。
当然,珠宝图不是静态的摆设 !珠宝图不是静态的摆设 !珠宝图不是静态的摆设!
我们可以拖动上面的每一个珠宝,来改变流中可观察对象的发射顺序:
我们拖动第一个时间轴——20, 40, 60
上的可观察对象,把20
这个珠宝拖到所有的珠宝前面(让其最先发射)。
依照amb()
操作符的定义,我们可以推断,输出会变为20, 40, 60
。截图验证一下:
当一个流被联通后,其他的流肿么办?先记住结论:未被选择的流将被调用dispose方法,也就是说,他们被终止了。
实例
HTML
<body> <input id="input1" type="text"> <input id="input2" type="text"> </body>
JavaScript
input1 = $('#input1'); input2 = $('#input2'); var source = Rx.Observable.amb( Rx.Observable.fromEvent(input1, 'click') .map(()=>'one'), Rx.Observable.fromEvent(input2, 'click') .map(()=>'two') );
上面例子中,amb()
中传入了两个点击事件流。事件流1,会在点击后发射字符串one
;事件流2,会在点击后发射字符串two
;
初始情况下,产生事件流1之后,事件流2不会再被输出;反之亦然,我们可以订阅amb()
产生的结果流:
var subscription = source.subscribe( function (x) { console.log(x); }, function (err) { console.log('Error: ' + err); }, function () { console.log('Completed'); });
具体可演示实例,可以进入amb()操作符演示
。订阅结果会在控制台中输出。
当然,你可以在充分理解了amb()
的原理之后修改可演示实例,验证自己的掌握程度。
题外话
上文提到过 Rx 是一种编程模式,几乎各个平台、语言栈都有实现。我们试着探讨下amb()
更宽泛地应用:
秒杀系统 :秒杀是一个高并发的场景,出现“多卖”是常态,“多卖”是由于秒杀商品的库存同步问题引起的。参与秒杀的用户呈竞争态,将请求分组后(比如100个一组),通过amd()
可以甄选出具有购买资格的用户:因为秒杀的产品逻辑是:谁手快,谁买到。
Observable.amb( 用户A的拍下请求, 用户B的拍下请求, 用户C的拍下请求, ... ).subscribe(function(user) { 执行购买逻辑,创建订单,打开支付工具 })
移动电话:假设同一时间多个人呼叫你,你接通了最先到达的来电,这段时间内你就只能和他(她、它)通话了,其余呼叫者将会接收到忙音(对不起,你所呼叫的用户正在通话中,请稍后再拨)。
Observable.amb( A来电, B来电, C来电, ... ).subscribe(function(call) { 通话吧啦吧啦 })
剧终