RxJS 中的觀察者和迭代器模式
阿新 • • 發佈:2020-08-04
### 目錄
- 前言
- 觀察者模式
- 迭代器模式
- RxJS 中兩種模式的結合和實現
- 小結
- 參考
### 1. 前言 RxJS 是一個庫,它通過使用`observable`(可觀察物件)序列來編寫非同步和基於事件的程式。其結合了`觀察者模式`、`迭代器模式`和`使用集合的函數語言程式設計`,以一種理想方式來管理事件序列所需要的一切。 本文將主要探討觀察者模式、迭代器模式以及它們如何在 RxJS 中被應用。
### 2. 觀察者模式 > 實現了生產者(事件的建立者)和消費者(事件的監聽者)的邏輯分離關係。
瀏覽器 DOM 事件的監聽和觸發應該是 Web 前端最典型的觀察者模式的實現。 ```js document.body.addEventListener('click', function listener(e) { console.log(e); }); document.body.click(); // 模擬使用者點選 ``` 監聽:通過`addEventListener`給 `document.body`節點繫結一個`click`事件的事件處理函式。 觸發:當用戶點選頁面(body)時,`body`節點將會觸發繫結的事件處理函式。 關係圖如下: ![Web事件的觀察者模式](https://img2020.cnblogs.com/blog/898684/202008/898684-20200804150423224-1837945848.png)
### 3. 迭代器模式 > 可以讓使用者通過特定的介面訪問集合中的每一個元素而不用瞭解底層的實現。
從 ES 6 開始,引入的一種新的遍歷機制——**迭代器**,其就是迭代器模式在 JavaScript 中的一種實現。在 JavaScript 中,迭代器是一個物件,它定義一個序列,並在終止時可能返回一個返回值。 更具體地說,迭代器是通過使用 `next()` 方法實現 [Iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol) (迭代器協議)的任何一個物件,該方法返回具有兩個屬性的物件: `value`和`done` ,其中`value`代表具體返回值,`done`表示是否已經迭代完畢。 [`String`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/String)、[`Array`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Array)、[`Map`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Map) 和 [`Set`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set) 等都是內建可迭代物件,它們的原型物件都擁有一個 [`Symbol.iterator`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) 方法。 ```js 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`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of) 迴圈來遍歷可迭代物件: ```js const arr = ['a', 'b']; for (let value of arr) { console.log(value); // a b } ``` `for-of`語法是為了方便遍歷可迭代物件,其內部實現呼叫的是`Symbol.iterator`方法,類似下面的程式碼: ```js 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(); } ```
迭代器的特點: 1. 訪問集合中的內容而不用瞭解底層的實現。 2. 提供了一個統一的介面遍歷不同的集合結構,從而支援同樣的演算法在不同的集合結構上進行操作。
### 4. RxJS 中兩種模式的結合和實現 RxJS 中包含兩個基本概念:**Observable **和 **Observer**。 **Observable** 作為可觀察物件(被觀察者),是一個可呼叫的未來值或事件的集合(非同步或同步資料流)。 **Observer** 作為觀察者,是一個回撥函式的集合,它知道如何去監聽由`Observable`提供的值。
`Observable`和`Observer`之間的訂閱釋出關係(觀察者模式)如下: 訂閱:`Observer` 通過 `Observable` 提供的 `subscribe()` 方法訂閱 `Observable`。 釋出:`Observable` 通過 `Observer` 提供的 `next` 方法向 `Observer` 釋出事件。 兩者關係的虛擬碼如下: ```js // 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()`,來處理異常和完成狀態。 ```js 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 訂閱-釋出例項: ```js import { Observable } from 'rxjs'; const observable = new Observable(function (observer) { // 通知觀察者 observer.next('a'); observer.next('b'); observer.complete(); // 將取消該觀察者的訂閱 // observer.error(new Error('err')); observer.next('c'); // 由於已經 complete,所以不會再發送 }); // 定義觀察者,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') } // 訂閱 Observable 並執行 const subscription = observable.subscribe(observer); // 將返回一個可取消的訂閱物件 subscription ``` 執行結果: ```console a b ```
### 5. 小結 **一句話概述 RxJS 中實現的觀察者+迭代器模式:**就是將觀察者`Observer`注入到可觀察物件`Observable`中,然後在可觀察物件`Observable`中通過呼叫`Observer`提供的 `next`、`complete`、`error` 方法處理流的不同狀態,以實現對資料流的一種順序訪問處理。
### 6. 參考 [RxJS 中文文件](https://cn.rx.js.org/) [Rx.js實現原理淺析](https://www.cnblogs.com/tangzhirong/p/74247
### 1. 前言 RxJS 是一個庫,它通過使用`observable`(可觀察物件)序列來編寫非同步和基於事件的程式。其結合了`觀察者模式`、`迭代器模式`和`使用集合的函數語言程式設計`,以一種理想方式來管理事件序列所需要的一切。 本文將主要探討觀察者模式、迭代器模式以及它們如何在 RxJS 中被應用。
### 2. 觀察者模式 > 實現了生產者(事件的建立者)和消費者(事件的監聽者)的邏輯分離關係。
瀏覽器 DOM 事件的監聽和觸發應該是 Web 前端最典型的觀察者模式的實現。 ```js document.body.addEventListener('click', function listener(e) { console.log(e); }); document.body.click(); // 模擬使用者點選 ``` 監聽:通過`addEventListener`給 `document.body`節點繫結一個`click`事件的事件處理函式。 觸發:當用戶點選頁面(body)時,`body`節點將會觸發繫結的事件處理函式。 關係圖如下: ![Web事件的觀察者模式](https://img2020.cnblogs.com/blog/898684/202008/898684-20200804150423224-1837945848.png)
### 3. 迭代器模式 > 可以讓使用者通過特定的介面訪問集合中的每一個元素而不用瞭解底層的實現。
從 ES 6 開始,引入的一種新的遍歷機制——**迭代器**,其就是迭代器模式在 JavaScript 中的一種實現。在 JavaScript 中,迭代器是一個物件,它定義一個序列,並在終止時可能返回一個返回值。 更具體地說,迭代器是通過使用 `next()` 方法實現 [Iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol) (迭代器協議)的任何一個物件,該方法返回具有兩個屬性的物件: `value`和`done` ,其中`value`代表具體返回值,`done`表示是否已經迭代完畢。 [`String`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/String)、[`Array`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Array)、[`Map`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Map) 和 [`Set`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set) 等都是內建可迭代物件,它們的原型物件都擁有一個 [`Symbol.iterator`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) 方法。 ```js 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`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of) 迴圈來遍歷可迭代物件: ```js const arr = ['a', 'b']; for (let value of arr) { console.log(value); // a b } ``` `for-of`語法是為了方便遍歷可迭代物件,其內部實現呼叫的是`Symbol.iterator`方法,類似下面的程式碼: ```js 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(); } ```
迭代器的特點: 1. 訪問集合中的內容而不用瞭解底層的實現。 2. 提供了一個統一的介面遍歷不同的集合結構,從而支援同樣的演算法在不同的集合結構上進行操作。
### 4. RxJS 中兩種模式的結合和實現 RxJS 中包含兩個基本概念:**Observable **和 **Observer**。 **Observable** 作為可觀察物件(被觀察者),是一個可呼叫的未來值或事件的集合(非同步或同步資料流)。 **Observer** 作為觀察者,是一個回撥函式的集合,它知道如何去監聽由`Observable`提供的值。
`Observable`和`Observer`之間的訂閱釋出關係(觀察者模式)如下: 訂閱:`Observer` 通過 `Observable` 提供的 `subscribe()` 方法訂閱 `Observable`。 釋出:`Observable` 通過 `Observer` 提供的 `next` 方法向 `Observer` 釋出事件。 兩者關係的虛擬碼如下: ```js // 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()`,來處理異常和完成狀態。 ```js 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 訂閱-釋出例項: ```js import { Observable } from 'rxjs'; const observable = new Observable(function (observer) { // 通知觀察者 observer.next('a'); observer.next('b'); observer.complete(); // 將取消該觀察者的訂閱 // observer.error(new Error('err')); observer.next('c'); // 由於已經 complete,所以不會再發送 }); // 定義觀察者,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') } // 訂閱 Observable 並執行 const subscription = observable.subscribe(observer); // 將返回一個可取消的訂閱物件 subscription ``` 執行結果: ```console a b ```
### 5. 小結 **一句話概述 RxJS 中實現的觀察者+迭代器模式:**就是將觀察者`Observer`注入到可觀察物件`Observable`中,然後在可觀察物件`Observable`中通過呼叫`Observer`提供的 `next`、`complete`、`error` 方法處理流的不同狀態,以實現對資料流的一種順序訪問處理。
### 6. 參考 [RxJS 中文文件](https://cn.rx.js.org/) [Rx.js實現原理淺析](https://www.cnblogs.com/tangzhirong/p/74247