一,响应式编程

响应式编程是一种关注于数据流(data streams)和变化传递(propagation of change)的异步编程方式。

1.1 异步编程

传统的编程方式是顺序执行的,必须在完成了上一个任务之后才能执行下一个任务。无论是提升机器的性能还是代码的性能,本质上都需要依赖上一个任务的完成。如果需要响应迅速,就得把同步执行的方式换成异步执行,方法执行变成消息发送。这样的优点是,当你有一堆不依赖于彼此的任务时,你可以同时启动所有任务,而不是等到每个任务完成后再启动下一个。这就是异步编程的方法,它是响应式编程的重要特性之一。

1.2 什么是数据流?

数据流是一种编程范式,数据流也被称为流处理或响应式编程。

传统上的程序指的是按照特定的顺序所执行的一系列操作,这通常被称为控制流或者命令式编程,这种程序侧重于命令,这与冯·诺依曼顺序编程的观点一致。

相比之下,数据流编程强调数据的传递,并将程序构建为一系列的连接。它显式地定义了输入和输出的连接操作,其功能类似于黑匣子。当一个操作的所有输入都有效时,它就会运行。因此,数据流语言本质上是并行的,可以在大型分布式系统中很好地工作。

举一个例子,App 的欢迎界面通常会需要加载广告,本地数据库等操作,这些都可以通过并行的方式来进行处理,全部成功之后才会进入主页,如下图:

android 函数式编程和响应式编程 响应式编程好处_android 函数式编程和响应式编程

通过这个例子,我们能够发现数据流的概念其实非常简单。它通过将不同的程序模块(每个程序模块可独立执行)连接起来,构建出并行的应用程序模型。

这里提到的不同的程序模块,在响应式编程中都被称为流。任何东西,数据库,共享内存,用户输入,变量等等都可以用流来表示。

1.3 变化传播

简单来说就是以一个数据流为输入,经过一系列的操作之后转换为另一个数据流,然后再分发给各个订阅者的过程。因为构建了数据之间的订阅依赖关系,将有助于自动传播改变的数据。

正是因为具备了以上这些特点,使用响应式编程可以很方便的表达数据流,同时相关的计算模型也能够自动变化并通过数据流进行传播。

响应式编程在处理 UI 事件,处理嵌套回调的异步事件,复杂的列表过滤等等方面都有很好的表现。

二,Reactive Extensions

Reactive Extensions(简称 ReactiveX) 是对响应式编程理念的一个实现。它最初是由微软的一个团队所开发的响应式扩展库(Reactive Extensions libraray,Rx),随后越来流行,目前已经支持了几乎全部的流行编程语言。

社区网站 reactivex.io 对其给出的定义是:

ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.
Rx 是一个使用可观测的序列来组成异步的、基于事件的程序的库。

定义中提到的序列即是指的数据序列,数据序列可以采用多种形式,例如来自文件或者Web服务的数据流,Web服务请求,系统通知或者用户输入的一系列事件。Rx 将所有这些数据序列表示为可观察序列 。应用程序可以订阅这些可观察序列,以在新数据到达时接收异步通知。(Rx 是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流)。

在响应式编程中,通过订阅数据流(在Rx中称为可观察序列)来向应用程序提供数据,应用程序在数据检索过程中是被动的:除了订阅了可观察的数据源之外,它不会主动轮询数据源,而只是对推送到它的数据做出反应。当流不再没有更多数据或者出错时,数据源也会向订阅者发送通知。

Reactive Extension采用的是这种推送(push)模式,它使得应用程序更具响应性。

ReactiveX 结合了观察者模式、迭代器模式和函数式编程的精华,为什么这么说呢?接着往下看:

现在假设有一个生产者 A 和一个消费者 B,生产者 A 持有一个可迭代的数据集合。当消费者 B 想要得到这个集合中的数据时,如果使用迭代器模式,消费者 B 就需要去从生产者 A 提供的迭代器中主动的读取数据,而如果使用的是观察者模式,数据将会由生产者 A 推送给消费者 B(本质上是使用回调之类的操作)。

这两种设计模式的区别就在于:迭代器模式中,消费者从生产者那里以同步的方式得到值,是消费者在控制何时取出数据。而在观察者模式中,生产者以异步的方式把值推给消费者,是生产者在控制消费者何时将接收到数据。

观察者模式适合使用在 UI 点击事件上,而迭代器模式的优势又在于能够使用一种更通用的方式来遍历不同类型的数据集合。

Reactive Extension(响应式扩展)结合了迭代器模式的思想,扩展了 GOF 观察者模式。简单来说就是 Rx 将可观察的数据类型和可迭代的数据类型统一成了一种名为 Observable 的新类型。

我们可以将 Observable 类视为与 Iterable 的 “拉” 操作功能相当的 “推” 操作。在 Iterable 中,消费者从生产者和线程中同步地提取值,而 Observable 中,只要值可用,生产者就会将值推送给消费者,这种方法更灵活,因为值可以同步或者异步到达。

Observables 和 Iterables(可迭代对象)共用一个相似的 API:在 Iterables 可以执行的许多操作也都同样可以在 Observables 上执行。不过由于 Observables 流的本质,所以并没有类似 Iterable.remove() 这样的方法。

Observables 支持异步推送多个值:

android 函数式编程和响应式编程 响应式编程好处_数据_02

在传统的 GOF 观察者模式缺少了这样两个语义(或者说接口):

  1. 生产者在没有更多数据的时候能够发出通知。
  2. 生产者在发生错误的时候能够发出通知。

所以为了更好复用 Iterable 接口,Rx 的 Observable 类扩展了 GOF 观察者模式,将 Observable async/push(异步推送)与 Iterable sync/pull(同步拉取)关联了起来。引入了两个新接口:

android 函数式编程和响应式编程 响应式编程好处_观察者模式_03

  • onCompleted():通知观察者Observable没有更多的数据。
  • onError():观察者有错误出现了。

同时 Rx 还提供了一些操纵符,让你可以使用声明式的方式来组合这些序列,而无须关注底层的实现,比如线程、同步、线程安全、并发数据结构和非阻塞I/O。
正是这种 Observable 模型让开发者可以像使用集合数据一样操作数据流,并对数据流进行诸如过滤,查询,组合等一系列操作。

2.1 总结Rx模式

  • 使用订阅模式
  • 创建:Rx 可以方便的创建事件流和数据流
  • 组合:Rx 使用查询式的操作符组合和变换数据流
  • 监听:Rx可以订阅任何可观察的数据流并执行操作
  • 简化代码
  • 函数式风格:对可观察的数据流使用无副作用的输入/输出函数,避免程序里错综复杂的状态
  • 简化代码:Rx 操作符有助于简化代码,避免嵌套
  • 异常处理机制:传统的 try/catch 无法处理异步计算,Rx 提供了合适的错误处理机制
  • 更容易处理同步与并发问题

参考:

ReactiveX/RxJava文档中文版响应式编程数据流https://docs.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh242985(v=vs.103)http://www.jonathanbeard.io/blog/2015/09/19/streaming-and-dataflow.htmlhttp://reactivex.io/intro.html