背景知识
Rxjs结合了响应式编程,函数式编程,观察者模式,迭代器模式等的异步编程应用库。要想对Rxjs有一定了解,需要我们先来了解这几个概念。
RxJS比较完整的理解应该是基于流的订阅者模式,而流的灵活性体现在可拆分和聚合,有了pipe管道的加入,流的可复用性增强,因此更容易对代码逻辑进行抽象。
函数式编程
函数式编程(Functional Programming,简称FP),是一种强调以函数使用为主的编程风格 ,也是一种范式,主要思想是想将复杂的运算分解成一系列可复用的的函数,逐层推导,不断渐进,直至完成运算。函数式编程是基于纯函数的。
- 函数式编程是一种强调以函数使用为主的软件开发风格 ,也是一种范式;
- 与面向对象编程(Object-oriented programming)和过程式编程(Procedural programming)并列的编程范式;
- 强调将计算过程分解成可复用的函数,典型例子就是map方法和reduce方法组合而成;
- 只有纯的、没有副作用的函数,才是合格的函数;
- 函数式编程必须以纯函数作为根基,目的是写出健壮的,声明式的代码;
在前端领域,我们能看到很多函数式编程的影子:ES6 中加入了箭头函数,Redux的combineReducers,React16.6 开始推出 React.memo(),使得 pure functional components 成为可能,16.8 开始主推 Hook,建议使用 pure function 进行组件编写等等。就连Java8也开始支持函数式编程。
响应式编程
响应式编程(Reactive Programming),它是一种基于事件的模型。在上面的异步编程模式中,我们描述了两种获得上一个任务执行结果的方式,一个就是主动轮询,我们把它称为 Proactive 方式。另一个就是被动接收反馈,我们称为 Reactive。简单来说,在 Reactive 方式中,上一个任务的结果的反馈就是一个事件,这个事件的到来将会触发下一个任务的执行。
流 Stream
作为响应式编程的核心,流的本质是一个按时间顺序排列进行中事件的序列集合。对于一流或多个流来说,我们可以对他们进行转化,合并等操作,生成一个新的流,在这个过程中,流是不可改变的,也就是只会在原来的基础返回一个新的stream。本质其实就是一个数据的集合,只是这些数据不一定是一开始就设定好的,而是随着时间而不断产生的。
观察者模式
观察者模式是一种行为设计模式,允许定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。也就是,定义一个对象与其他对象之间的一种依赖关系,当对象发生某种变化的时候,依赖它的其它对象都会得到通知,是一对多的关系。
也就是说,观察者模式定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
有人说,观察者模式又叫发布订阅模式(Publish/Subscribe),也有人说,观察者模式和发布订阅模式是两种模式。我觉得这是因为看待的角度不同,其实这两种模式都可以统称为“观察者模式”,而发布订阅模式只观察它感兴趣的事件。一般来说,当遇到“发布订阅模式”时,就称为“发布订阅模式”,非发布订阅模式时,那就称它为“观察者模式”。
优点
- 支持简单的广播通信,自动通知所有已经订阅过的对象;
- 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用;
缺点
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
写个简单的观察者模式的例子吧。
class Subject {
constructor() {
this.observers = [];
}
addObserver(ob) {
if (!this.observers.some((o) => o === ob)) {
this.observers.push(ob);
}
}
notify(...args) {
this.observers.forEach((ob) => {
ob && ob.receive(...args);
});
}
removeObserver(ob) {
let index = this.observers.indexOf(ob);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
}
class Observer {
constructor(name){
this.name = name;
}
receive(...args){
console.log(`${this.name}收到消息了`, ...args);
}
}
let ob1 = new Observer('Tom');
let ob2 = new Observer('Jerry');
let sb = new Subject();
sb.addObserver(ob1);
sb.addObserver(ob2);
sb.notify('来上班');
sb.removeObserver(ob1);
sb.notify('来上班');
迭代器模式
迭代器(Iterator)模式又叫游标(Sursor)模式,迭代器模式是一种设计模式,迭代器模式的核心,就是实现统一遍历的接口 。 它提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
优点
- 简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标取得,但用户需要在对集合了解的前提下,自行遍历对象,但是对于 hash 表来说,用户遍历起来就比较麻烦。而引入迭代器方法后,用户用起来就简单的多了;
- 封装性良好,用户只需要得到迭代器就可以遍历,而不用去关心遍历算法;
缺点
- 遍历过程是一个单向且不可逆的遍历;
Rxjs是基于观察者模式和迭代器模式以函数式编程思维来实现的。