异步数据流的响应式编程库Rxjs(二)背景知识篇

115 阅读5分钟

背景知识

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是基于观察者模式和迭代器模式以函数式编程思维来实现的。