RxJs 介紹(更新中)

jopen 8年前發布 | 36K 次閱讀 Reactive JavaScript開發

“Reactive Programming是神馬?”

互聯網上充斥著很多操蛋的解釋。 維基百科 又寬泛又玄乎。 Stackoverflow 教科書式的解釋非常不適合信任 Reactive Manifesto 聽起來像是給給項目經理或者是銷售的匯報。 微軟的 Rx 定義 “Rx = Observables + LINQ + Schedulers” 太重并且太微軟化了,讓人看起來不知所云。“響應”、“變化發生”這些術語無法很好地闡釋Reactive Programming的顯著特點,聽起來和你熟悉的MV*、編程語言差別不大。 當然,我的視角也是基于模型和變換的,要是脫離了這些概念,一切都是無稽之談了。

那么我要開始吧啦吧啦了。

Reactive programming 是針對異步數據流的編程。

一定程度而言,Reactive programming并不算新的概念。事件總線、點擊事件都是異步流。開發者可以觀測這些異步流,并調用特定的邏輯對它們進行處理。使用Reactive如同開掛:你可以創建點擊、懸停之類的任意流。通常流廉價(點擊一下就出來一個)而無處不在,種類豐富多樣:變量,用戶輸入,屬性,緩存,數據結構等等都可以產生流。舉例來說:微博回文(譯者注:比如你關注的微博更新了)和點擊事件都是流:你可以監聽流并調用特定的邏輯對它們進行處理。

基于流的概念,Reactive賦予了你一系列神奇的函數工具集,使用他們可以合并、創建、過濾這些流。一個流或者一系列流可以作為另一個流的輸入。你可以_合并_

兩個流,從一堆流中_過濾_你真正感興趣的那一些,將值從一個流_映射_到另一個流。

如果流是Reactive programming的核心,我們不妨從“點擊頁面中的按鈕”這個熟悉的場景詳細地了解它。

流是包含了 有時序,正在進行事件 的序列,可以發射(emmit)值(某種類型)、錯誤、完成信號。流在包含按鈕的瀏覽器窗口被關閉時發出完成信號。

我們 異步地 捕獲發射的事件,定義一系列函數在值被發射后,在錯誤被發射后,在完成信號被發射后執行。有時,我們忽略對錯誤,完成信號地處理,僅僅關注對值的處理。對流進行監聽,通常稱為 訂閱 ,處理流的函數是觀測者,流是被觀測的主體。這就是 觀測者設計模式

教程中,我們有時會使用ASCII字符來繪制圖表:

--a---b-c---d---X---|->

a, b, c, d 是數據流發射的值
X 是數據流發射的錯誤
| 是完成信號
---> 是時序軸

嗶嗶完了,我們來點新的,不然很快你就感覺到寂寞了。我們將把原來的點擊事件流轉換為新的點擊事件流。

首先我們創建一個計數流來表明按鈕被點擊的次數。在Reactive中,每一個流都擁有一些列方法,例如 map , filter , scan 等等。當你在流上調用這些方法,例如 clickStream.map(f) ,會返回基于點擊事件流的 新的流 ,同時原來的點擊事件流并不會被改變,這個特性被稱為 不可變性(immutability) 。不可變性與Reactive配合相得益彰,如同美酒加咖啡。我們可以鏈式地調用他們: clickStream.map(f).scan(g)

  clickStream: ---c----c--c----c------c--->
               vvvvv map(c becomes 1) vvvvv
               ---1----1--1----1------1--->
               vvvvvvvvv  scan(+) vvvvvvvvv
counterStream: ---1----2--3----4------5--->

map(f) 函數對原來的流使用我們出入的 f 函數進行轉換,并生成新的流。在上面的例子中,我們將每一次點擊映射為數字1。 scan(g) 函數將所有流產生的值進行匯總,通過傳入 x = g(accumulated, current) 函數產生新的值, g 是簡單的求和函數。最后 counterStream 在點擊發生后發射點擊事件發生的總數。

為了展示Reactive的真正力量,我們舉個例子:你想要“兩次點擊”事件的流,或者是“三次點擊”,或者是n次點擊的流。深呼吸一下,試著想想怎么用傳統的命令、狀態式方法來解決。我打賭這個這會相當操蛋,你會搞些變量來記錄狀態,還要搞些處理時延的機制。

如果用Reactive來解決,太他媽簡單了。實際上 4行代碼就可以搞定 。先不要看代碼,不管你是菜鳥還是牛逼,使用圖表來思考可以使你更好地理解構建這些流的方法。

灰色框里面的函數會把一個流轉換成另外一個流。首先我們把點擊打包到list中,如果點擊后消停了250毫秒,我們就重新打包一個新的list(顯然 buffer(stream.throttle(250ms)) 就是用來干這個的,不明白細節沒有關系,反正是demo嘛)。我們在列表上調用 map() ,將列表的長度映射為一個整數的流。最后,我們通過 filter(x >= 2) 過濾掉整數 1 。哈哈:3個操作就生成了我們需要的流,現在我們可以訂閱(監聽)這個流,然后來完成我們需要的邏輯了。

通過這個例子,我希望你能感受到使用Reactive的牛逼之處了。這僅僅是冰山一角。你可以在不同地流上(比如API響應的流)進行同樣的操作。同時,Reactive還提供了許多其他實用的函數。

“我要在今后的項目中使用Reactive programming嗎?”

Reactive Programming 提高了編碼的抽象程度,你可以更好地關注在商業邏輯中各種事件的聯系避免大量細節而瑣碎的實現,使得編碼更加簡潔。

使用Reactive Programming,將使得數據、交互錯綜復雜的web、移動app開發收益更多。10年以前,與網頁的交互僅僅是提交表單、然后根據服務器簡單地渲染返回結果這些事情。App進化得越來越有實時性:修改表單中一個域可以同步地更新到后端服務器。“點贊”信息實時地在不同用戶設備上同步。

現代App中大量的實時事件創造了更好的交互和用戶體驗,披荊斬棘需要利劍在手,Reactive Programming就是你手中的利劍。

Reactive Programming編程思想(附實例)

我們將從實例可以深入Reactive Programming的編程思想,文章末尾,一個完整地實例應用會被構建,你也會理解整個過程。

我選擇 JavaScriptRxJS 作為構建的基礎, 大多開發者都熟悉JavaScript語言。 Rx* library family 在各種語言和平臺都是實現 ( .NET , Java , Scala , Clojure , JavaScript , Ruby , Python , C++ , Objective-C/Cocoa , Groovy , 等等)。無論你選擇在哪個平臺或者那種語言實踐Reactive Programming,你都將從本教程中受益。

微博(推ter)簡易版“你可能感興趣的人”

微博主頁,有一個組件會推薦給你那些你可能感興趣的人。

我們的Demo將使用這個場景,關注下面這些主要特性:

  • 頁面打開后,通過API加載數據展示3個你可能感興趣的用戶賬號

  • 點擊“刷新”按鈕,重新加載三個新的用戶賬號

  • 在一個用戶賬號上點擊’x’ 按鈕,清除當前這個賬戶,重新加載一個新的賬戶

  • 每行展示賬戶的信息和這個賬戶主頁的鏈接

其他特性和按鈕我們暫且忽略,由于推ter在最近關閉了公共API授權接口,我們選擇Github作為代替,展示GitHub用戶的賬戶。實例中我們使用該接口 獲取GitHub用戶 .

如果你希望先睹為快,完成后的代碼已經發布在了 Jsfiddle

請求&響應

這個問題使用Rx怎么解?,呵呵,我們從Rx的箴言開始:_神馬都是流_。首先我們做最簡單的部分——頁面打開后通過API加載3個賬戶的信息。分三步走:(1)發一個請求(2)獲得響應(3)依據響應渲染頁面。那么,我們先使用流來表示請求。我靠,表示個請求用得著嗎?不過千里之行始于足下。

頁面加載時,僅需要一個請求。所以這個數據流只包含一個簡單的反射值。稍后,我們再研究如何多個請求出現的情況,現在先從一個請求開始。

--a------|->

a是字符串 'https://api.github.com/users'

這個流中包含了我們希望請求的URL地址。一旦這個請求事件發生,我們可以獲知兩件事情:請求流發射值(字符串URL)的時間就是請求需要被執行的時間,請求需要請求的地址就是請求流發射的值。

在Rx*中構建一個單值的流很容易。官方術語中把流稱為“觀察的對象”(”Observable”),因為流可以被觀察、訂閱,這么稱呼顯得很蠢,我自己把他們稱為_stream_。

var requestStream = Rx.Observable.just('https://api.github.com/users');

目前這個攜帶字符串的流沒有其他操作,我們需要在這個流發射值之后,做點什么:通過 訂閱 這個流來實現。

requestStream.subscribe(function(requestUrl) {
  // execute the request
  jQuery.getJSON(requestUrl, function(responseData) {
    // ...
  });
}

我們采用了jQuery的Ajax回調 (假設讀著已經了解jQuery ajax回調 ) 來處理異步請求操作。 且慢,Rx天生就是處理 異步 數據流的,

為何不把請求的響應作為一個攜帶數據的流呢? 么么噠,概念上沒有問題,我們就來操作一下。

requestStream.subscribe(function(requestUrl) {
  // 執行異步請求
  var responseStream = Rx.Observable.create(function (observer) {
    jQuery.getJSON(requestUrl)
    .done(function(response) { observer.onNext(response); })
    .fail(function(jqXHR, status, error) { observer.onError(error); })
    .always(function() { observer.onCompleted(); });
  });

  responseStream.subscribe(function(response) {
    // 業務邏輯
  });
}

使用 Rx.Observable.create() 方法可以自定義你需要的流。你需要明確通知觀察者(或者訂閱者)數據流的到達( onNext() ) 或者錯誤的發生( onError() )。這個實現中,我們封裝了jQuery 的異步 Promise。 那么Promise也是可觀察對象嗎?

冰狗,你猜對啦!

來自: http://hao.jser.com/archive/9081/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!