16

起因

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()

介绍一下牛逼的 珠宝图
amb珠宝图
从左到右的箭头,代表时间轴。|代表可观察对象(流)发出了完成信号。
轴上的每一个珠宝代表流发射的

下方amd那个层是处理操作符,本图意味着所有操作符以上的流,都会经过操作符的处理(操作符以上的流为操作符的操作数);

最下方,是操作符处理后的输出结果
y = f(x),其中x表示输入流,f()是操作符,y是最后的输出流。

观察上面的珠宝图,1, 2, 3这条时间轴上的可观察对象发射了值1,所以amb()选择了它作为最终输出的可观察对象。接下来如果它被订阅,订阅者会依次收到1,23

当然,珠宝图不是静态的摆设珠宝图不是静态的摆设珠宝图不是静态的摆设
我们可以拖动上面的每一个珠宝,来改变流中可观察对象发射顺序

我们拖动第一个时间轴——20, 40, 60上的可观察对象,把20这个珠宝拖到所有的珠宝前面(让其最先发射)。
依照amb()操作符的定义,我们可以推断,输出会变为20, 40, 60。截图验证一下:

amb珠宝图拖动

当一个流被联通后,其他的流肿么办?先记住结论:未被选择的流将被调用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) {
    通话吧啦吧啦
})

剧终


mumuzhenzhen
1.7k 声望557 粉丝

« 上一篇
RxJS 教程