Rxjs 01-認識Rxjs

ReactiveX combines the Observer pattern with the Iterator pattern and functional programming with collections to fill the need for an ideal way of managing sequences of events. ReactiveX將觀察者模式、迭代器模式和函數編程與集合結合起來,以知足管理事件序列的理想方式的須要。html

根據官方定義,RxJS 是基於觀察者模式和迭代器模式以函數式編程思惟來實現的,那麼咱們先了解一下這幾個概念。express

函數式編程(Functional Programming)

什麼是函數式編程 ?

Functional Programming 是一種編程範式(programming paradigm),就像Object-oriented Programming(OOP)同樣,就是一種寫程式的方法論,這些方法論告訴咱們如何思考及解決問題。編程

函數式編程關心數據的映射,命令式編程關心解決問題的步驟.bash

這裏的映射就是數學上函數的概念——一種東西和另外一種東西之間的對應關係, 簡單說Functional Programming 核心思想就是作運算處理,並用function 來思考問題.markdown

函數式編程基本要素

函式爲一等公民(First Class)

所謂一等公民是指跟其它對象具備同等的地位,也就是說函數可以被賦值給變量,也可以被看成參數傳入另外一個函數,也可看成一個函數的返回值。數據結構

// 函數可以被賦值給變量
var hello = function() {}

// 函數看成參數傳入另外一個函數
fetch('www.google.com')
.then(function(response) {}) // 匿名 function 被傳入 then()

// 看成一個函數的返回值
var a = function(a) {
	return function(b) {
	  return a + b;
	}; 
}
複製代碼

Expression, no Statement

Functional Programming都是表達式(Expression)不會是語句(Statement)。 基本區分表達式與語句:併發

  • 表達式是一個運算過程,必定會有返回值,例如執行一個function, 聲明一個變量。
  • 語句則是表現某個行爲,例如賦值給一個變量, for循環,if判斷

有時候表達式也可能同時是合法的語句,這裏只講基本的判斷方法。若是想更深刻了解其中的差別,能夠看這篇文章Expressions versus statements in JavaScript dom

純函數(Pure Function)

純函數是這樣一種函數,即相同的輸入,永遠會獲得相同的輸出,並且沒有任何可觀察的反作用(side effect)ide

舉個簡單的例子,slicesplice函數式編程

var arr = [1, 2, 3, 4, 5];

arr.slice(0, 3); // [1, 2, 3]

arr.slice(0, 3); // [1, 2, 3]

arr.slice(0, 3); // [1, 2, 3]
複製代碼

這裏能夠看到slice無論執行幾回,返回值都是相同的,而且除了返回一個值以外並無作任何事,因此slice就是一個pure function

var arr = [1, 2, 3, 4, 5];

arr.splice(0, 3); // [1, 2, 3]

arr.splice(0, 3); // [4, 5]

arr.slice(0, 3); // []
複製代碼

這裏咱們換成用splice,由於splice每執行一次就會影響arr的值,致使每次結果都不一樣,這就很明顯不是一個pure function

什麼是反作用(side effect)

反作用指一個function作了跟自己運算返回值沒有關係的事,好比說修改某個全域變數,或是修改傳入參數的值,甚至是執行console.log都算是反作用。

Functional Programming 強調沒有反作用,也就是function 要保持純粹,只作運算並返回一個值,沒有其餘額外的行爲。

這裏列舉幾個常見的反作用:

  • 更改文件系統
  • 發送一個 http 請求
  • 可變數據(random)
  • 打印/log
  • 獲取用戶輸入
  • DOM 查詢

歸納來說,只要是跟函數外部環境發生的交互就都是反作用——這一點可能會讓你懷疑無反作用編程的可行性。函數式編程的哲學就是假定反作用是形成不正當行爲的主要緣由, 這並非說,要禁止使用一切反作用,而是說,要讓它們在可控的範圍內發生。

函數式編程優點

  • 可讀性高: 經過一系列的函數封裝過程,代碼變得很是的簡潔且可讀性極高
  • 可維護性高: 由於Pure function等特性,執行結果不依賴外部狀態,且不會對外部環境有任何操做,這使得單元測試和調試都更容易
  • 易於並行處理: 因爲不共享外部狀態,不會形成資源爭用(Race condition),也就不須要用鎖來保護可變狀態,也就不會出現死鎖,這樣能夠更好地併發起來。

觀察者模式(Observer Pattern)

觀察者模式,即發佈-訂閱模式,它定義了一個一對多的依賴關係,讓一個或多個觀察者對象監聽一個主題對象。這樣一來,當被觀察者狀態發生改變時,須要通知相應的觀察者,使這些觀察者對象可以自動更新。

關鍵要素

主題

主題是觀察者觀察的對象,一個主題必須具有下面三個特徵。

  • 持有監聽的觀察者的引用
  • 支持增長和刪除觀察者
  • 主題狀態改變,主動通知觀察者

觀察者

當主題發生變化,收到通知後進行具體的處理

這裏舉一個例子來講明,牛奶送奶站就是主題,訂奶客戶爲監聽者,客戶從送奶站訂閱牛奶後,會天天收到牛奶。若是客戶不想訂閱了,能夠取消,之後就不會收到牛奶。

根據上面的說明,咱們能夠簡單實現一個被觀察者:

class Subject {
  constructor() {
    this.observerCollection = [];
  }

  registerObserver(observer){
    this.observerCollection.push(observer)
  }
  unRegisterObserver(observer){
    this.observerCollection.splice(this.observer.findIndex(observer), 1)
  }

  notifyObservers(message){
    this.observerCollection.forEach(observer => {
      observer.notify(message);
    })
  }
}
複製代碼

鬆耦合

  • 觀察者增長或刪除無需修改主題的代碼,只需調用主題對應的增長或者刪除的方法便可。
  • 主題只負責通知觀察者,但無需瞭解觀察者如何處理通知。舉個例子,送奶站只負責送遞牛奶,不關心客戶是喝掉仍是洗臉。
  • 觀察者只需等待主題通知,無需觀察主題相關的細節。仍是那個例子,客戶只需關心送奶站送到牛奶,不關心牛奶由哪一個快遞人員,使用何種交通工具送達。

迭代器模式(Iterator Pattern)

迭代器模式(Iterator)提供了一種方法順序訪問一個集合對象中各個元素,而又不暴露該對象的內部表示,迭代器模式能夠把迭代的過程從業務邏輯中分離出來,在使用迭代器模式以後,即便不關心對象的內部構造,也能夠按順序訪問其中的每一個元素。

Iterator 的遍歷過程是這樣的:

  1. 建立一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。

  2. 第一次調用指針對象的next方法,能夠將指針指向數據結構的第一個成員。

  3. 第二次調用指針對象的next方法,指針就指向數據結構的第二個成員。

  4. 不斷調用指針對象的next方法,直到它指向數據結構的結束位置。

可參考ES6系列--7. 可迭代協議和迭代器協議中關於迭代器的介紹。

JavaScript 中像 Array、Set、Map 等都屬於內置的可迭代類型, 能夠經過 iterator方法來獲取一個迭代對象,調用迭代對象的 next 方法將獲取一個元素對象,以下示例:

var arr = [1, 2, 3];

var iterator = arr[Symbol.iterator]();

iterator.next();
// { value: 1, done: false }
iterator.next();
// { value: 2, done: false }
iterator.next();
// { value: 3, done: false }
iterator.next();
// { value: undefined, done: true }

複製代碼

遍歷迭代器可使用下面的方法。

while(true) {
  let result;
  try {
    result = iterator.next();
  } catch (error) {
    handleError(error); // 錯誤處理
  }
  if (result.done) {
    handleCompleted(); // 已完成以後的處理
  }
  doSomething(result.value);
}
複製代碼

上面的代碼主要對應三種狀況:

  • 獲取下一個值(next):調用next方法能夠將元素一個個的返回,這樣就支持了返回屢次值。
  • 已完成(complete):當沒有更多值時,next返回元素中的donetrue
  • 錯誤處理(error):當 next 方法執行時報錯,則會拋出 error 事件,因此能夠用 try catch 包裹 next 方法處理可能出現的錯誤。

下一篇開始介紹Observable 和 observer。

相關文章
相關標籤/搜索