30 天精通 RxJS (01):認識 RxJS

YuTao發表於2019-05-10

原作者:一名來自臺灣的小夥子,熱愛學習新技術,喜歡 JS 與 Functional Programming,熱衷於把困難的技術用簡單的語言闡述,歡迎來到我的文章。

原文

在網頁中存取資料都是非同步(Async)的,比如說我們想從後端拿到一組資料,要先傳送一個請求,然後必須等到資料回來,再執行對這個資料的操作。這就是一個非同步的行為,而隨著網頁需求的複雜化,我們所寫的 JavaScript 就有各鍾針對非同步行為的寫法,例如使用 callback 或是 Promise 物件甚至是新的語法糖 async/await —— 但隨著應用需求越來越複雜,撰寫非同步的程式碼仍然非常困難。

非同步常見的問題

  • 竟態條件 (Race Condition)
  • 記憶體洩漏 (Memory Leak)
  • 複雜的狀態 (Complex State)
  • 異常處理 (Exception Handling)

Race Condition

當我們對同一個資源同時做多次的非同步存取時,就可能發生 Race Condition 的問題。比如說我們發了一個 Request 更新使用者資料,然後我們又立即傳送另一個 Request 取得使用者資料,這時第一個 Request 和第二個 Request 先後順序就會影響到最終接收到的結果不同,這就是 Race Condition。

Memory Leak

Memory Leak 是最常被大家忽略的一點。原因是在傳統網站開發中,我們每次跳轉都是整個頁面重新載入,重新執行 JavaScript,所以不太需要關注記憶體的問題!但是當我們希望將網站做得像軟體時,這件事就變得很重要。例如做 SPA (Single Page Application) 網站時,我們是通過 JavaScript 來達到切換頁面的內容,這時如果有對 DOM 註冊監聽事件,而沒有在適當的時機把監聽的事件移除,就有可能造成 Memory Leak。比如說在 A 頁面監聽 body 的 scroll 事件,但頁面切換時,沒有把 scroll 的監聽事件移除。

Complex State

當有非同步行為時,函式的狀態就會變得非常複雜!比如說我們有一個付費使用者才能播放的視訊,首先可能要先獲取這部視訊的資訊,接著我們要在播放時去驗證使用者是否有許可權播放,而使用者也有可能再按下播放後又立即按了取消,而這些都是非同步執行,這時就有會各種複雜的狀態需要處理。

Exception Handling

JavaScript的try/catch可以捕捉同步的異常,但非同步的程式就沒這麼容易,尤其當我們的非同步行為很複雜時,這個問題就愈加明顯。

各種不同的API

我們除了要面對非同步會遇到的各種問題外,還需要煩惱很多不同的API

  • DOM Events
  • XMLHttpRequest
  • Fetch
  • WebSockets
  • Server Send Events
  • Service Worker
  • Node Stream
  • Timer

上面列的API都是非同步的,但他們都有各自的API及寫法!如果我們使用RxJS,上面所有的API都可以通過RxJS來處理,就能用同樣的API操作(RxJS的API)。

這裡我們舉一個例子,假如我們想要監聽點選事件(click event),但點選一次之後不再監聽。

原生JavaScript

var handler = (e) => {
	console.log(e);
	document.body.removeEventListener('click', handler); // 結束監聽
}

// 註冊監聽
document.body.addEventListener('click', handler);
複製程式碼

使用 Rx

Rx.Observable
	.fromEvent(document.body, 'click') // 註冊監聽
	.take(1) // 只取一次
	.subscribe(console.log);
複製程式碼

大致上能看得出來我們在使用RxJS後,不管是針對DOM Event還是上面列的各種API我們都可以通過RxJS的API來做資料操作,示例中用 take(n)來設定只取一次,之後就釋放記憶體。

說了這麼多,其實就是簡單一句話

在面對日益複雜的問題,我們需要一個更好的解決方法。

RxJS 基本介紹

RxJS是一套由Observable sequences來組合非同步行為事件基礎函式的Library!

可以把RxJS想成處理非同步行為的Lodash。

也可以被稱為Functional Reactive Programming,更切確地說是指Functional Programming及Reactive Programming兩個程式設計思想的結合。

RxJS確實是Functional Programming跟Reactive Programming的結合,但能不能稱為Functional Reactive Programming(FRP)一直有爭議。

Rx在官網上特別指出,有時這會被稱為FRP,但這其實是個“誤稱”。

簡單說FRP是操作隨著時間連續性改變的數值,而Rx則比較像是操作隨著時間發出的離散數值,這個部份讀者不用分得太細,因為FRP的定義及解釋一直存在著歧異,也有眾多大神為此爭論,如下

AndréStaltz: Rx著名的推廣者,也是RxJS 5主要貢獻者之一,同時是Cycle.js的作者。Staltz特別寫了一篇文章解釋為什麼Rx不能說是FRP但他仍然稱其為FRP。

Juan Gomez:曾在Netflix工作,目前任職於Fitbit,經常出現在國外演討會,主要寫Android。Juan Gomez在Droidcon NYC 2015的演講中特別提出他堅持稱Rx為FRP。

Evan Czaplicki:任職於NoRedInk,Elm的作者。Evan在StrangeLoop 2014的演講中,特別為現在各種FRP的不同解釋做分類。

關於Reactive Extension(Rx)

Rx最早是由微軟開發的LinQ擴充套件出來的開源專案,之後主要由社群的工程師貢獻,有多種語言支援,也被許多科技公司所採用,如Netflix,Trello,Github,Airbnb…等。

Functional Reactive Programming

Functional Reactive Programming是一種程式設計思想(programming paradigm),舉個例子,像OOP就是一種程式設計思想,OOP告訴我們要使用物件的方式來思考問題,以及編寫程式。而Functional Reactive Programming其實涵蓋了Reactive Programming及Functional Programming兩種程式設計思想。

Functional Programming

Functional Programming大部分的人應該多少都有接觸過,這也是Rx學習過程中的重點之一,我們之後會花兩章的篇幅來細講Functional Programming。如果要用一句話來總結Functional Programming,那就是用function來思考我們的問題,以及撰寫程式

在下一篇文章會更深入的講解Functional Programming

Reactive Programming

很多人一談到Reactive Programming就會直接聯想到是在講RxJS,但實際上Reactive Programming仍是一種程式設計思想,在不同的場景都有機會遇到,而非只存在於RxJS,尤雨溪(Vue的作者)就曾在twitter對此表達不滿!

30 天精通 RxJS (01):認識 RxJS

Reactive Programming簡單來說就是當變數或資料發生變化時,由變數或資料自動告訴我發生變動了

這句話看似簡單,其實背後隱含兩件事

  • 當發生變動=>非同步:不知道什麼時候會發生變動,反正變動時要跟我說

  • 由變數自動告知我=>不用通知我的每一步程式碼

由於最近很紅的Vue.js底層就是用Reactive Programming的概念實現,能很好的舉例,讓大家理解什麼是Reactive Programming!

當我們在使用vue開發時,只要一有繫結的變數發生改變,相關的變數及頁面也會跟著變動,而開發者不需要寫這其中如何通知發生變化的每一步程式碼,只需要專注在發生變化時要做什麼事,這就是典型的Reactive Programming(記得必須是由變數或資料主動告知!)

Vue.js在做two-ways data binding是通過ES5 definedProperty的getter/setter。每當變數發生變動時,就會執行getter/setter從而收集有改動的變數,這也被稱為依賴收集

Rx基本上就是上述的兩個觀念的結合,這個部份在看完之後的文章,會有更深的體悟。

今日小結

今天這篇文章主要是帶大家瞭解為什麼我們需要RxJS,以及RxJS的基本介紹。若讀者還不太能吸收本文的內容,可以過一段時間後再回來看這篇文章會有更深的體會,或是在下方留言給我!

原文

相關文章