04-解锁反应式编程:Observable

154 阅读5分钟

Observable之于RxJS,就好比Promise之于async/await。

尽管W3C已经正式接纳了Observable,并将其作为ECMA Script原生支持的类型之一,RxJS采用了自己实现的Observable,这与浏览器原生支持的类型有些许不同,扩展了更多的能力,同时也能更好的兼容低版本浏览器。

Observable并不复杂,其核心部分代码甚至可以简化到数十行代码以内。但由于Observable采用了一种比较底层的代码抽象模型,对于偏向业务实现的业务开发者理解起来有一点门槛。

从底层原理上来说,Observable实现了两种常见的软件领域设计模式(Design Pattern),分别是观察者模式(Observer Pattern)和迭代器模式(Iterator Pattern),让我们首先从理解这两种模式开始。

观察者模式(Observer Pattern)


观察者模式是一种非常常见的软件设计模式,尤其是对于现代前端开发者而言,我们使用Vue或者React几乎每天都在与观察者模式打交道。比如,事件监听程序:


function eventHandler() {
  // do something
}

document.getElementById('eleId').addEventListener('eventName', eventHandler)

简单来说,在观察者模式下,我们有两类主要的对象,分别是观察者与被观察者。这两类对象之间通过简单数据通信,从而实现设计上的解耦。

观察者模式为我们提供了一种很健壮的方式来实现应用开发中的数据分发需求,并且很容易支持单播和广播。


graph

%% 单播

Dispatcher1 --> Observer

%% 广播

Dispatcher2 --> Observer1 & Observer2

更有经验的开发者通常会将观察者模式与发布/订阅模式进行比较,这也是容易让人混淆的概念之一。尽管严格来说,他们之间存在些许的差异,但对于我们理解Observable并不会造成障碍。如果有兴趣的读者,可以进一步了解他们之间的差异细节。

Observer vs Pub-Sub Pattern (ahmedshamim.com)

迭代器模式


在迭代器模式中,我们创建一类特定类型的对象,叫做迭代器(Iterator)。它类似于一个指针,或者在数据库系统中,也称之为游标。他帮助我们指向一个集合/数组类型(sequence)的数据结构,并能顺序获取集合中的所有元素(element)。

所有高级编程语言都支持循环结构,比如for,while,这与迭代器模式存在一些差别。其中,最重要的区别在于借助于迭代器,我们可以在需要时才去拿序列中的值,这实现了一种延迟运算(Lazy evaluation)的能力。

ES 6 之后,JavaScript语言原生支持了迭代器对象。

var array = [1,2,3]
var iterator = arr[Symbol.iterator]()

iterator.next()
Console: {value: 1, done: false}

JavaScript中的Iterator仅有一个next方法,而调用next方法仅会返回两种可能的结果:

  1. 在迭代器到达最后一个元素之前:{ done: false, value: element }
  2. 在迭代器到达最后一个元素之后:{ done: true, value: undefined }

为了更好的在语言层面支持函数式编程的风格,在ECMAScript 中的迭代器实现并没有采用类似Python语言中的规范,也就是Iterator迭代器在获取到所有的元素之后,并不会以抛出异常(Exception)的方式结束,而是会返回相同的数据结构,以达到编程处理风格的一致性。

尽管迭代器看似简单,但是却是基于序列/集合实现函数式编程中的其他高阶运算的基础能力,比如投影(map)、过滤(filter)、排序(orderBy)等等。

关于延迟计算(Lazy evaluation)

延迟计算是一种高级的程序执行策略(evaluation strategy),在有的地方也被叫做晚绑定,他让我们在真正需要某个表达式的值时才会执行运算,从而提供了一种声明式的编程风格,允许程序将表达式声明与表达式运行分离,提供了在运行时动态组装表达式的能力,这也是函数式编程的基础。

其他高阶编程语言同样支持延迟计算的特性,其中最为人熟知的当属C# LINQ,它正是RxJS为代表的ReactiveX扩展的思想起源。

Observable


前面我们了解的两种设计模式,事实上他们具备某种程度上的共通性,那就是他们都是一种渐进式(progressive) 的数据风格。不同之处在于观察者模式中,由被观察者决定何时向观察者发送数据,是一种典型的推模式(push),而在迭代器模式中,是由迭代器来决定何时从指定的序列中获取数据,是一种拉模式(pull)。

pkqeOsK.png

Observable是这两种设计模式的结合,它同时具备了数据生产者向数据消费者进行数据推送的特性,也能像迭代器一样按顺序处理序列中的数据。

更准确的说,Observable是一个具有迭代器外观(Facade)的数据生产者,其中的元素会随时间的变化按顺序推送。

要理解Observable,最重要的就是以下关键概念:

  • 迭代(iterator、map、filter、lazy-load...)
  • 推送(Observe、单播、广播、多播...)
  • 时间(同步/异步、排队、插队...)

如果你觉得本文对你有些许启发,请持续关注我的公众号“戈伊星球”吧!