Rx学习—理解Observable(2)

上次我们把 Observable 和函数进行类比,从函数的角度分析了 Observable 的一些特性,这次我们换一个角度,从流的角度理解 Observable。

从事件机制说起

Event

在浏览器中,事件机制是用户交互的基础,本质是程序各个组成部分之间的一种通信方式(或者叫观察者模式)。当事件源被触发,会调用相应的事件处理函数执行,并把事件对象(Event 对象,持有事件源的一些信息)传递给事件处理函数。没有Rx之前,事件一般是互相独立,各自调用的。

增加时间的维度

现在我们增加一个时间维度,在时间坐标上把同一个按钮上的单个事件点连成一条线:

image-20190329105443416

有了时间维度,这条线看起来会是流动的,所以把他叫做一个流,接下来我们具体了解一下关于流的概念。

流stream

创建数据流

RxJS提供了各种API来创建数据流,我们先使用 create 进行自定义创建:

1
2
3
4
5
6
const foo = Rx.Observable.create((observer) => {
let i = 0
setInterval(() => observer.next(i++), 1000)
})

foo.subscribe(i => console.log(i))

可以看到我们创建了一个定时器定时触发,并在定时器中定时回调 observer,吐出一个返回值,相当于我们通过迭代器模拟了一个定时发生的点击事件(事件源),返回值相当于 Event(事件对象),而 observer 则相当于 EventListener(事件处理的监听器),单个事件串联起来就形成了流。

还记得我们上次提到的生产者和消费者的概念吗?上例中,迭代器作为生产者不断生成数据形成流,observer 作为消费者等待回调,虽然 Observable 本质上还是一个函数,但是从形式上来看,Observable 就像一个管道对接了生产者和消费者。

数据的管道

数据流这个词,很多时候,是从data-flow翻译过来的,但flow跟stream是不一样的,我的理解是:flow只关注一个大致方向,而stream是受到更严格约束的,它更像是在无形的管道里面流动。

而 Observable 就是限制 stream 流动的无形管道中的一种。,从整个过程来看, Observable 对接了生产者和消费者,也就是作为管道对接了流,从生产者角度来说,Observable 是使得数据流可以被观测的一种特殊函数,于是有了一个新的名字:可观测对象,而从消费者的角度来说,它就是流的代表,可以直接被叫做流。

Everything is a stream

不只是 Events,我们可以把一切输入都当做数据流来处理,比如说:

  • 用户操作
  • 网络响应
  • 定时器
  • Worker

产生新流

当产生了一个流后,我们可以通过操作符(Operator)对这个流进行一系列加工操作,然后产生一个新的流:

1
2
3
4
5
6
Rx.Observable.fromEvent(window, 'click')
.map(e => 1)
.scan((total, now) => total + now)
.subscribe(value => {
console.log(value)
})

map 把流转换成了一个每次产生1的新流,然后 scan 类似 reduce,也会产生一个新流,最后这个流被订阅。最终实现了每次点击累加1的效果。

可以用一个效果图来表示该过程:

img

lodash for stream

这时候回头看,其实RxJS在事件处理的路上已经走得太远了,从事件到流,它被称为lodash for events,倒不如说是lodash for stream更贴切,它提供的这些操作符也确实可以跟lodash媲美。

总结

在两篇文章中,我们分别从函数和流的角度了解了 Observable。本质上讲,Observable 是一种特殊的函数, 同时也是 RxJS 的一个核心类型,可以收集单个事件形成流来统一处理。

我觉得复杂的东西都有多种特性,就像光的波粒二象性一样,我们学习时不要拘泥于某种特性去归类,因为其可能具有不同事物的多种特性,需要我们从不同角度来描述和理解。

最后再分享一篇文章,希望能帮助更好的理解Observable:如何从头开始创建 Observable

参考链接

RxJS 入门指引和初步应用

RxJS基础教程