RxJS 中的观察者和迭代器模式
目录
- 前言
- 观察者模式
- 迭代器模式
- RxJS 中两种模式的结合和实现
- 小结
- 参考
1. 前言
RxJS 是一个库,它通过使用observable
(可观察对象)序列来编写异步和基于事件的程序。其结合了观察者模式
、迭代器模式
和使用集合的函数式编程
,以一种理想方式来管理事件序列所需要的一切。
本文将主要探讨观察者模式、迭代器模式以及它们如何在 RxJS 中被应用。
2. 观察者模式
实现了生产者(事件的创建者)和消费者(事件的监听者)的逻辑分离关系。
浏览器 DOM 事件的监听和触发应该是 Web 前端最典型的观察者模式的实现。
document.body.addEventListener('click', function listener(e) {
console.log(e);
});
document.body.click(); // 模拟用户点击
监听:通过addEventListener
给 document.body
节点绑定一个click
事件的事件处理函数。
触发:当用户点击页面(body)时,body
节点将会触发绑定的事件处理函数。
关系图如下:
3. 迭代器模式
可以让用户通过特定的接口访问集合中的每一个元素而不用了解底层的实现。
从 ES 6 开始,引入的一种新的遍历机制——迭代器,其就是迭代器模式在 JavaScript 中的一种实现。在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next()
方法实现 Iterator protocol (迭代器协议)的任何一个对象,该方法返回具有两个属性的对象: value
和done
,其中value
代表具体返回值,done
表示是否已经迭代完毕。
String
、Array
、Map
和 Set
等都是内置可迭代对象,它们的原型对象都拥有一个 Symbol.iterator
方法。
const arr = ['a', 'b'];
const iterator = arr[Symbol.iterator](); // 获取迭代器对象
iterator.next(); // { value: 'a', done: false }
iterator.next(); // { value: 'b', done: false }
iterator.next(); // { value: undefined, done: true }
我们常常用for-of
循环来遍历可迭代对象:
const arr = ['a', 'b'];
for (let value of arr) {
console.log(value); // a b
}
for-of
语法是为了方便遍历可迭代对象,其内部实现调用的是Symbol.iterator
方法,类似下面的代码:
const arr = ['a', 'b'];
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value); // a b
result = iterator.next();
}
迭代器的特点:
- 访问集合中的内容而不用了解底层的实现。
- 提供了一个统一的接口遍历不同的集合结构,从而支持同样的算法在不同的集合结构上进行操作。
4. RxJS 中两种模式的结合和实现
RxJS 中包含两个基本概念:**Observable **和 Observer。
Observable 作为可观察对象(被观察者),是一个可调用的未来值或事件的集合(异步或同步数据流)。
Observer 作为观察者,是一个回调函数的集合,它知道如何去监听由Observable
提供的值。
Observable
和Observer
之间的订阅发布关系(观察者模式)如下:
订阅:Observer
通过 Observable
提供的 subscribe()
方法订阅 Observable
。
发布:Observable
通过 Observer
提供的 next
方法向 Observer
发布事件。
两者关系的伪代码如下:
// Observer
const observer = {
next(value) {
console.log(value);
}
};
// Observable
function Observable (observer) {
setTimeout(()=>{
observer.next('A');
}, 1000);
}
// subscribe
Observable(observer);
从上可知,所谓订阅,就是将观察者Observer
注入到可观察对象Observable
中。
在 RxJS 中,Observer
除了有 next
方法来接收 Observable
的事件外,还提供了另外的两个方法:error()
和 complete()
,来处理异常和完成状态。
const observer = {
next(value) { /* 处理值 */ },
error(err) { /* 处理异常 */ },
complete() { /* 处理已完成态 */ }
};
结合迭代器 Iterator 来理解Observer
的三个方法:
- next():
Observer
通过提供next
方法来接受Observable
流(集合),是一种push
形式(推送)。- 对比
Iterator
,则是通过调用iterator.next()
拿值,是一种pull
的形式(拉取)。
- 对比
- complete():当不再有新的值发出时,将触发
Observer
的complete
方法。- 对比
Iterator
,则是在next()
的返回结果中的done
为true
时,则表示complete
。
- 对比
- error():当处理事件中出现异常时,通过
try-catch
捕获异常,Observer
提供error
方法来接收错误进行统一处理。
一个简单的 RxJS 订阅-发布实例:
import { Observable } from 'rxjs';
// 定义观察者,next、complete、error 方法处理流的不同状态
const observer = {
next: (value) => console.log(value),
error: err => console.error('Observer got an error: ' + err),
complete: () => console.log('Observer got a complete notification')
}
// 定义可观察对象
const observable = new Observable(function (observer) {
// 通知观察者
observer.next('a');
observer.next('b');
observer.complete(); // 将取消该观察者的订阅
// observer.error(new Error('err'));
observer.next('c'); // 由于已经 complete,所以不会再发送
});
// 订阅 Observable 并执行
const subscription = observable.subscribe(observer); // 将返回一个可取消的订阅对象 subscription
执行结果:
a
b
Observer got a complete notification
至于为什么调用了complete()
方法后就会取消该观察者的订阅?通过查看源码可知,这是因为 RxJS 在观察者Observer
进行订阅时,对其 complete()
和error()
方法都进行了一定hack
,调用时都会触发取消订阅unsubscribe
的操作。
5. 小结
一句话概述 RxJS 中实现的观察者+迭代器模式:就是将观察者Observer
注入到可观察对象Observable
中,然后在可观察对象Observable
中通过调用观察者Observer
提供的 next
、complete
、error
方法处理流的不同状态,以实现对数据流的一种顺序访问处理。