摘要:響應(yīng)式編程具有很強(qiáng)的表現(xiàn)力,舉個例子來說,限制鼠標(biāo)重復(fù)點擊的例子。在響應(yīng)式編程中,我把鼠標(biāo)點擊事件作為一個我們可以查詢和操作的持續(xù)的流事件。這在響應(yīng)式編程中尤其重要,因為我們隨著時間變換會產(chǎn)生很多狀態(tài)片段。迭代器模式的另一主要部分來自模式。
Rxjs 響應(yīng)式編程-第一章:響應(yīng)式
Rxjs 響應(yīng)式編程-第二章:序列的深入研究
Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序
Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整的Web應(yīng)用程序
Rxjs 響應(yīng)式編程-第五章 使用Schedulers管理時間
Rxjs 響應(yīng)式編程-第六章 使用Cycle.js的響應(yīng)式Web應(yīng)用程序
現(xiàn)實世界相當(dāng)混亂:事件不按照順序發(fā)生,應(yīng)用崩潰,網(wǎng)絡(luò)不通。幾乎沒有應(yīng)用是完全同步的,所以我們不得不寫一些異步代碼保持應(yīng)用的可響應(yīng)性。大多數(shù)的時候是很痛苦的,但也并不是不可避免。
現(xiàn)代應(yīng)用需要超級快速的響應(yīng)速度,并且希望能夠不漏掉一個字節(jié)的處理來自不同數(shù)據(jù)源的數(shù)據(jù)。然而并沒有現(xiàn)成的解決方案,因為它們不會隨著我們添加并發(fā)和應(yīng)用程序狀態(tài)而擴(kuò)展代碼變得越來越復(fù)雜。
本章向您介紹反應(yīng)式編程,這是一種自然,簡單的方法處理異步代碼的方式。我會告訴你事件的流程 - 我們稱之為Observables - 是處理異步代碼的一種很好的方式。
然后我們將創(chuàng)建一個Observable,看看響應(yīng)式思維和RxJS是怎么樣改善現(xiàn)有技術(shù),讓你成為更快樂,更多高效的程序員。
讓我們從一個小的響應(yīng)性RxJS程序開始。 這個程序需要通過單擊按鈕檢索來自不同來源的數(shù)據(jù),它具有以下要求:
它必須統(tǒng)一來自使用不同源的JSON結(jié)構(gòu)
最終結(jié)果不應(yīng)包含任何副本
為了避免多次請求數(shù)據(jù),用戶不能重復(fù)點擊按鈕
使用RxJS,我們的代碼類似這樣:
var button = document.getElementById("retrieveDataBtn"); var source1 = Rx.DOM.getJSON("/resource1").pluck("name"); var source2 = Rx.DOM.getJSON("/resource2").pluck("props", "name"); function getResults(amount) { return source1.merge(source2) .pluck("names") .flatMap(function(array) { return Rx.Observable.from(array); }) .distinct() .take(amount); } var clicks = Rx.Observable.fromEvent(button, "click"); clicks.debounce(1000) .flatMap(getResults(5)) .subscribe( function(value) { console.log("Received value", value); }, function(err) { console.error(err); }, function() { console.log("All values retrieved!"); } );
不要擔(dān)心不理解這里的代碼。只要關(guān)注于成果即可。你看到的第一件事是我們使用更少的代碼實現(xiàn)更多的功能。我們通過使用Observable來實現(xiàn)這一目標(biāo)。
Observable表示數(shù)據(jù)流。程序也可以可以主要表示為數(shù)據(jù)流。在前面的示例中,兩個遠(yuǎn)程源是Observables,用戶點擊鼠標(biāo)也是如此。實際上,我們的程序本質(zhì)上是一個由按鈕的單擊事件構(gòu)成的Observable,我們把它轉(zhuǎn)變成獲得我們想要的結(jié)果。
響應(yīng)式編程具有很強(qiáng)的表現(xiàn)力,舉個例子來說,限制鼠標(biāo)重復(fù)點擊的例子。想象一下我們使用我們使用promise和callback實現(xiàn)這個功能是有多復(fù)雜:我們需要每秒重置一下點擊次數(shù),并且在用戶點擊之后每秒都要保存點擊狀態(tài)。但是這樣子,對于這個小功能來說就顯得過于復(fù)雜了,并且所寫代碼與業(yè)務(wù)功能并沒有直觀的聯(lián)系。為了彌補(bǔ)基礎(chǔ)代碼庫的功能不足,在一個大型應(yīng)用中,這些很小的復(fù)雜功能會增加的非??臁?/p>
通過響應(yīng)式編,我們使用debounce方法來限制點擊流次數(shù)。這樣就保證每次點擊的間隔時間至少1s,忽略1s之間的點擊次數(shù)。我們不關(guān)心內(nèi)部如何實現(xiàn),我們只是表達(dá)我們希望代碼執(zhí)行的操作,而不是如何操作。
這就變得更有趣了。接下來,您將看到反應(yīng)式編程如何幫助我們提高課程效率和表現(xiàn)力。
電子表格是可響應(yīng)的讓我們從這樣一個響應(yīng)性系統(tǒng)的典型例子開始考慮:點子表格。我們都是使用過吧,但我們很少停下來思考它們是多么令人震驚的直觀。假設(shè)我們在電子表格的單元格A1中有一個值,然后我們可以在電子表格中的其他單元格中引用它,并且每當(dāng)我們更改A1時,每個依賴于A1的單元格都會自動更新與A1同步。
這些操作對我們感覺很自然,我們并不會去告訴計算機(jī)去根據(jù)A1更新單元格或者如何更新;這些單元格就自動這樣子做了。在點子表格中,我們只需要簡單的聲明我們需要處理的問題,不用操心計算機(jī)如何處理。
鼠標(biāo)輸入作為streams理解如何把事件作為流,我們回想一下本章開頭的那個程序。在那里,我們使用鼠標(biāo)點擊作為用戶點擊時實時生成的無限事件流。這個想法起源于Erik Meijer,也就是Rxjs的作者。他認(rèn)為:你的鼠標(biāo)就是一個數(shù)據(jù)庫。
在響應(yīng)式編程中,我把鼠標(biāo)點擊事件作為一個我們可以查詢和操作的持續(xù)的流事件。想象成流而不是一個孤立的事件,這種想法開辟了一種全新的思考方式。我們可以在其中操縱尚未創(chuàng)建的整個值的流。
好好想想。這種方式有別于我們以往的編程方式,之前我們把數(shù)據(jù)存儲在數(shù)據(jù)庫,或者數(shù)組并且等待這些數(shù)據(jù)可用之后在使用它們。如果它們尚不可用(舉個例子:一個網(wǎng)絡(luò)請求),我們只能等它們好了才可以使用。
我們可以將流視為所在由時間而不是存儲位置分開的數(shù)組。無論是時間還是存儲位,我們都有元素序列:
將您的程序視為流動的數(shù)據(jù)序列是理解的RxJS程序的關(guān)鍵。這需要一些練習(xí),但并不難。事實上,大多數(shù)我們在任何應(yīng)用程序中使用的數(shù)據(jù)都可以表示為序列。
序列查詢讓我們使用傳統(tǒng)javascript傳統(tǒng)的事件綁定技術(shù)來實現(xiàn)一個鼠標(biāo)點擊流。要記錄鼠標(biāo)點擊的x和y坐標(biāo),我們可以這樣寫:
ch1/thinking_sequences1.js
document.body.addEventListener("mousemove", function(e) { console.log(e.clientX, e.clientY); });
此代碼將按順序打印每次鼠標(biāo)單擊的x坐標(biāo)和y坐標(biāo)。
輸出如下:
252 183 211 232 153 323 ...
看起來像一個序列,不是嗎? 當(dāng)然,問題在于操縱事件并不像操縱數(shù)組那么容易。 例如,如果我們想要更改前面的代碼,使其僅記錄前10次位于屏幕右側(cè)的單擊事件(相當(dāng)隨機(jī)的目標(biāo)),我們會寫像這樣的東西:
var clicks = 0; document.addEventListener("click", function registerClicks(e) { if (clicks < 10) { if (e.clientX > window.innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener("click", registerClicks); } });
為了滿足我們的要求,我們通過引入一個全局變量作為擴(kuò)展?fàn)顟B(tài)來記錄當(dāng)前點擊數(shù)。 我們還需要使用嵌套的條件來檢查兩個不同的條件。當(dāng)我們完成時,我們必須注銷事件,以免泄漏內(nèi)存。
副作用和外部狀態(tài)如果一個動作在其發(fā)生的范圍之外產(chǎn)生影響,我們稱之為一方副作用。更改函數(shù)外部的變量,打印到控制臺或更新數(shù)據(jù)庫中的值,這些都是副作用。例如改變函數(shù)內(nèi)部的變量是安全的,但是如果該變量超出了我們函數(shù)的范圍,那么其他函數(shù)也可以改變它的值,這就意味著這個功能不再受控制,因為你無法預(yù)測外部會對這個變量作何操作。所以我們需要跟蹤它,添加檢查以確保它的變化符合我們的預(yù)期。但是這樣子添加的代碼其實與我們程序無關(guān),確增加程序的復(fù)雜度也更容易出錯。雖然副作用總是會有的,但是我們應(yīng)該努力減少。這在響應(yīng)式編程中尤其重要,因為我們隨著時間變換會產(chǎn)生很多狀態(tài)片段。所以避免外部狀態(tài)和副作用是貫穿本書一條宗旨。
我們設(shè)法滿足了我們的簡單要求,但是為了實現(xiàn)這樣一個簡單的目標(biāo),最終得到了相當(dāng)復(fù)雜的代碼。對于首次查看它的開發(fā)人員來說,不容易懂且維護(hù)代碼很困難。 更重要的是,因為我們?nèi)匀恍枰4嫱獠孔菜晕覀兒苋菀自谖磥戆l(fā)展出玄妙的錯誤。
在這種情況下我們想要的只是查詢點擊的“數(shù)據(jù)庫”。如果我們是使用關(guān)系數(shù)據(jù)庫,我們使用聲明性語言SQL:
SELECT x, y FROM clicks LIMIT 10
如果我們將點擊事件流視為可以查詢和轉(zhuǎn)變的數(shù)據(jù)源,該怎么辦?畢竟,它與數(shù)據(jù)庫沒有什么不同,都是一個可以處理數(shù)據(jù)的東西。我們所需要的只是一個為我們提供抽象概念的數(shù)據(jù)類型。
輸入RxJS及其Observable數(shù)據(jù)類型:
Rx.Observable.fromEvent(document, "click") .filter(function(c) { return c.clientX > window.innerWidth / 2; }) .take(10) .subscribe(function(c) { console.log(c.clientX, c.clientY) })
這段代碼功能同之前,它可以這樣子解讀:
創(chuàng)建一個Observable的點擊事件,并過濾掉在點擊事件上發(fā)生屏幕左側(cè)的點擊。然后只在控制臺打印前10次點擊的坐標(biāo)。
注意即使您不熟悉代碼也很容易閱讀,也沒有必要創(chuàng)建外部變量來保持狀態(tài)。這樣使我們的代碼是自包含的,不容易產(chǎn)生bug。所以也就沒必要去清除你的狀態(tài)。我們可以合并,轉(zhuǎn)換或者單純的傳遞Observables。我們已經(jīng)將不容易處理的事件轉(zhuǎn)變?yōu)橛行螖?shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)與數(shù)組一樣易于使用,但更加靈活。
在下一節(jié),我們將看到使Observables如此強(qiáng)大的原理。
觀察者和迭代者要了解Observable的來源,我們需要查看他們的基礎(chǔ):Observer和Iterator軟件模式。在本節(jié)中我們將快速瀏覽它們,然后我們將看到Observables如何結(jié)合,簡單而有力。
觀察者模式對于軟件開發(fā)人員來說,很難不聽到Observables就想起觀察者模式。在其中我們有一個名為Producer的對象,內(nèi)部保留訂閱者的列表。當(dāng)Producer對象發(fā)生改變時,訂閱者的update方法會被自動調(diào)用。(在觀察者模式的大部分解釋中,這個實體被叫做Subject,為了避免大家和RxJs的自己Subject混淆,我們稱它為Producer)。
ch1/observer_pattern.js
function Producer() { this.listeners = []; } Producer.prototype.add = function(listener) { this.listeners.push(listener); }; Producer.prototype.remove = function(listener) { var index = this.listeners.indexOf(listener); this.listeners.splice(index, 1); }; Producer.prototype.notify = function(message) { this.listeners.forEach(function(listener) { listener.update(message); }); };
Producer對象在實例的偵聽器中保留一個動態(tài)的Listener列表,每當(dāng)Producer更新的時候都會調(diào)用其notify方法。在下面的代碼中,我們創(chuàng)建了兩個對象來監(jiān)聽
notifie,一個Producer的實例。
ch1/observer_pattern.js
// Any object with an "update" method would work. var listener1 = { update: function(message) { console.log("Listener 1 received:", message); } }; var listener2 = { update: function(message) { console.log("Listener 2 received:", message); } }; var notifier = new Producer(); notifier.add(listener1); notifier.add(listener2); notifier.notify("Hello there!");
當(dāng)我們運(yùn)行這個程序的時候:
Listener 1 received: Hello there! Listener 2 received: Hello there!
當(dāng)notifier更新內(nèi)部狀態(tài)的時候,listener1和listener2都會被更新。這些都不需要我們?nèi)ゲ傩摹?/p>
我們的實現(xiàn)很簡單,但它說明了觀察者模式允許觀察者和監(jiān)聽器解耦。
迭代器模式Observable的另一主要部分來自Iterator模式。一個Iterator是一個為消費者提供簡單的遍象它內(nèi)容的方式,隱藏了消費者內(nèi)部的實現(xiàn)。
Iterator接口很簡單。它只需要兩個方法:next()來獲取序列中的下一個項目,以及hasNext()來檢查是否還有項目序列。
下面是我們?nèi)绾尉帉懸粋€對數(shù)字?jǐn)?shù)組進(jìn)行操作的迭代器,并且只返回divisor參數(shù)的倍數(shù)的元素:
ch1/iterator.js
function iterateOnMultiples(arr, divisor) { this.cursor = 0; this.array = arr; this.divisor = divisor || 1; } iterateOnMultiples.prototype.next = function() { while (this.cursor < this.array.length) { var value = this.array[this.cursor++]; if (value % this.divisor === 0) { return value; } } }; iterateOnMultiples.prototype.hasNext = function() { var cur = this.cursor; while (cur < this.array.length) { if (this.array[cur++] % this.divisor === 0) { return true; } } return false; };
我們可以這樣子使用我們的迭代器:
ch1/iterator.js
var consumer = new iterateOnMultiples([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3); console.log(consumer.next(), consumer.hasNext()); // 3 true console.log(consumer.next(), consumer.hasNext()); // 6 true console.log(consumer.next(), consumer.hasNext()); // 9 false
迭代器非常適合封裝任何類型數(shù)據(jù)結(jié)構(gòu)的遍歷邏輯。 正如我們在前面的例子中看到的那樣,迭代器在處理不同類型的數(shù)據(jù)的時候就會變得很有趣,或者在運(yùn)行的時候做配置,就像我們在帶有divisor參數(shù)的示例中所做的那樣。
Rx模式和Observable雖然Observer和Iterator模式本身就很強(qiáng)大,但是兩者的結(jié)合甚至更好。 我們稱之為Rx模式,命名為
在Reactive Extensions庫之后。我們將在本書的其余部分使用這種模式。
Observable序列或簡單的Observable是Rx模式的核心。Observable按順序傳遞出來它的值 - 就像迭代器一樣 - 而不是消費者要求它傳出來的值。這個和觀察者模式有相同之處:得到數(shù)據(jù)并將它們推送到監(jiān)聽器。
pull和push在編程中,基于推送的行為意味著應(yīng)用程序的服務(wù)器組件向其客戶端發(fā)送更新,而不是客戶端必須輪詢服務(wù)器以獲取這些更新。這就像是說“不要打電話給我們; 我們會打電話給你。“
RxJS是基于推送的,因此事件源(Observable)將推動新值給消費者(觀察者),消費者卻不能去主動請求新值。
更簡單地說,Observable是一個隨著時間的推移可以使用其數(shù)據(jù)的序列。Observables,也就是Observers的消費者相當(dāng)于觀察者模式中的監(jiān)聽器。當(dāng)Observe訂閱一個Observable時,它將在序列中接收到它們可用的值,而不必主動請求它們。
到目前為止,似乎與傳統(tǒng)觀察者沒有太大區(qū)別。 但實際上有兩個本質(zhì)區(qū)別:
Observable在至少有一個Observer訂閱它之前不會啟動。
與迭代器一樣,Observable可以在序列完成時發(fā)出信號。
使用Observables,我們可以聲明如何對它們發(fā)出的元素序列做出反應(yīng),而不是對單個項目做出反應(yīng)。我們可以有效地復(fù)制,轉(zhuǎn)換和查詢序列,這些操作將應(yīng)用于序列的所有元素。
創(chuàng)建Observables有幾種方法可以創(chuàng)建Observable,創(chuàng)建函數(shù)是最明顯的一種。 Rx.Observable對象中的create方法接受一個Observer參數(shù)的回調(diào)。 該函數(shù)定義了Observable將如何傳出值。這是我們?nèi)绾蝿?chuàng)建一個簡單的Observable:
var observable = Rx.Observable.create(function(observer) { observer.onNext("Simon"); observer.onNext("Jen"); observer.onNext("Sergi"); observer.onCompleted(); // We are done });
當(dāng)我們訂閱此Observable時,它通過在其偵聽器上調(diào)用onNext方法來發(fā)出三個字符串。 然后它調(diào)用onCompleted來表示序列已完成。 但是我們究竟如何訂閱Observable呢?我們使用Observers來做這件事情。
第一次接觸ObserversObservers監(jiān)聽Observables。每當(dāng)Observable中觸發(fā)一個事件,它都會在所有Observers中調(diào)用相關(guān)的方法。
Observers有三種方法:onNext,onCompleted和onError:
onNext????相當(dāng)于觀察者模式中的update。 當(dāng)Observable發(fā)出新值時調(diào)用它。請注意該名稱如何反映我們訂閱序列的事實,而不僅僅是離散值。
onCompleted????表示沒有更多可用數(shù)據(jù)。 調(diào)用onCompleted后,對onNext的進(jìn)一步調(diào)用將不起作用。
onError????在Observable中發(fā)生錯誤時調(diào)用。 在調(diào)用之后,對onNext的進(jìn)一步調(diào)用將不起作用
以下是我們創(chuàng)建基本觀察者的方法:
var observer = Rx.Observer.create( function onNext(x) { console.log("Next: " + x); }, function onError(err) { console.log("Error: " + err); }, function onCompleted() { console.log("Completed"); } );
Rx.Observer對象中的create方法接受onNext,onCompleted和onError情況的函數(shù),并返回一個Observer實例。這三個函數(shù)是可選的,您可以決定要包含哪些函數(shù)。例如,如果我們訂閱無限序列(例如點擊按鈕(用戶可以永久點擊)),則永遠(yuǎn)不會調(diào)用onCompleted處理程序。 如果我們確信序列不能出錯(例如,通過從數(shù)組中生成一個Observable),我們就不需要onError方法了。
使用Observable進(jìn)行Ajax調(diào)用我們還沒有對Observables做過任何實用的事情。如何創(chuàng)建一個檢索遠(yuǎn)程內(nèi)容的Observable?為此,我們將使用Rx.Observable.create包裝XMLHttpRequest對象。
function get(url) { return rxjs.Observable.create(function(observer) { // Make a traditional Ajax request var req = new XMLHttpRequest(); req.open("GET", url); req.onload = function() { if (req.status == 200) { // If the status is 200, meaning there have been no problems, // Yield the result to listeners and complete the sequence observer.next(req.response); observer.completed(); } else { // Otherwise, signal to listeners that there has been an error observer.error(new Error(req.statusText)); } }; req.onerror = function() { observer.error(new Error("Unknown Error")); }; req.send(); }); } // Create an Ajax Observable var test = get("/api/contents.json");
在前面的代碼中,get函數(shù)使用create來包裝XMLHttpRequest。如果HTTP GET請求成功,我們emit它的內(nèi)容并結(jié)束序列(我們的Observable只會發(fā)出一個結(jié)果)。 否則,我們會emit一個錯誤。在最后一行,我們傳入一個url進(jìn)行調(diào)用。 這將創(chuàng)建Observable,但它不會發(fā)出任何請求。這很重要:Observable在至少有一個觀察者描述它們之前不會做任何事情。 所以讓我們要這樣子做:
// Subscribe an Observer to it test.subscribe( function onNext(x) { console.log("Result: " + x); }, function onError(err) { console.log("Error: " + err); }, function onCompleted() { console.log("Completed"); } );
首先要注意的是,我們沒有像之前的代碼那樣顯式創(chuàng)建Observer。大多數(shù)時候我們都會使用這個更短的版本,我們在Observable中使用這三個訂閱Observer案例的函數(shù):next,completed和error。
subscribe然后一切就緒。在subscribe之前,我們只是聲明了Observable和Observer將如何交互。只有當(dāng)我們調(diào)用subscribe方法時,一切才開始運(yùn)行。
始終會有一個Operator在RxJS中,轉(zhuǎn)換或查詢序列的方法稱為Operator。Operator位于靜態(tài)Rx.Observable對象和Observable實例中。在我們的示例中,create就是一個這樣的Operator。
當(dāng)我們必須創(chuàng)建一個非常具體的Observable時,create是一個很好的選擇,但是RxJS提供了許多其他Operator,可以很容易地為常用源創(chuàng)建Observable。
讓我們再看看前面的例子。對于像Ajax請求這樣的常見操作,通常有一個Operator可供我們使用。 在這種情況下,RxJS DOM庫提供了幾種從DOM相關(guān)源創(chuàng)建Observable的方法。由于我們正在執(zhí)行GET請求,我們可以使用Rx.DOM.Request.get,然后我們的代碼就變成了這個:
Rx.DOM.get("/api/contents.json").subscribe( function onNext(data) { console.log(data.response); }, function onError(err) { console.error(err); } );
rxjs-dom本身支持的rxjs版本比較舊,例子只能做為示意
這段代碼與我們之前的代碼完全相同,但我們不必創(chuàng)建XMLHttpRequest的包裝器: 它已經(jīng)存在了。另請注意,這次我們省略了onCompleted回調(diào),因為我們不打算在Observable complete時做出反應(yīng)。我們知道它只會產(chǎn)生一個結(jié)果,我們已經(jīng)在onNext回調(diào)中使用它了。
在本書中我們將使用這樣的大量便利操作符。這都是基于rxjs本身的能量,這也正式rxjs強(qiáng)大的地方之一。
一種可以約束全部的數(shù)據(jù)類型在RxJS程序中,我們應(yīng)該努力將所有數(shù)據(jù)都放在Observables中,而不僅僅是來自異步源的數(shù)據(jù)。 這樣做可以很容易地組合來自不同來源的數(shù)據(jù),例如現(xiàn)有數(shù)組與回調(diào)結(jié)果,或者XMLHttpRequest的結(jié)果與用戶觸發(fā)的某些事件。
例如,如果我們有一個數(shù)組,其項目需要與來自其他地方的數(shù)據(jù)結(jié)合使用,最好將此數(shù)組轉(zhuǎn)換為Observable。(顯然,如果數(shù)組只是一個不需要組合的中間變量,則沒有必要這樣做。)在本書中,您將了解在哪些情況下值得將數(shù)據(jù)類型轉(zhuǎn)換為Observables。
RxJS為operators提供了從大多數(shù)JavaScript數(shù)據(jù)類型創(chuàng)建Observable的功能。 讓我們回顧一下你將一直使用的最常見的:數(shù)組,事件和回調(diào)。
從數(shù)組創(chuàng)建Observable我們可以使用通用的operators將任何類似數(shù)組或可迭代的對象轉(zhuǎn)換為Observable。 from將數(shù)組作為參數(shù)并返回一個包含他所有元素的Observable。
Rx.Observable .from(["Adrià", "Jen", "Sergi"]) .subscribe( function(x) { console.log("Next: " + x); }, function(err) { console.log("Error:", err); }, function() { console.log("Completed"); } );
from是和fromEvent一起,是RxJS代碼中最方便和最常用的operators之一。
從JavaScript事件創(chuàng)建Observable當(dāng)我們將一個事件轉(zhuǎn)換為一個Observable時,它就變成了一個可以組合和傳遞的第一類值。 例如,這是一個Observable,只要它移動就會傳初鼠標(biāo)指針的坐標(biāo)。
var allMoves = Rx.Observable.fromEvent(document, "mousemove") allMoves.subscribe(function(e) { console.log(e.clientX, e.clientY); });
將事件轉(zhuǎn)換為Observable會將事件從之前的事件邏輯中釋放出來。更重要的是,我們可以基于原始的Observables創(chuàng)建新的Observable。這些新的是獨立的,可用于不同的任務(wù)。
var movesOnTheRight = allMoves.filter(function(e) { return e.clientX > window.innerWidth / 2; }); var movesOnTheLeft = allMoves.filter(function(e) { return e.clientX < window.innerWidth / 2; }); movesOnTheRight.subscribe(function(e) { console.log("Mouse is on the right:", e.clientX); }); movesOnTheLeft.subscribe(function(e) { console.log("Mouse is on the left:", e.clientX); });
在前面的代碼中,我們從原始的allMoves中創(chuàng)建了兩個Observable。 這些專門的Observable只包含原始的過濾項:movesOnTheRight包含發(fā)生在屏幕右側(cè)的鼠標(biāo)事件,movesOnTheLeft包含發(fā)生在左側(cè)的鼠標(biāo)事件。 它們都沒有修改原始的Observable:allMoves將繼續(xù)發(fā)出所有鼠標(biāo)移動。 Observable是不可變的,每個應(yīng)用于它們的operator都會創(chuàng)建一個新的Observable。
從回調(diào)函數(shù)創(chuàng)建Observable如果您使用第三方JavaScript庫,則可能需要與基于回調(diào)的代碼進(jìn)行交互。 我們可以使用fromCallback和fromNodeCallback兩個函數(shù)將回調(diào)轉(zhuǎn)換為Observable。Node.js遵循的是在回調(diào)函數(shù)的第一個參數(shù)傳入錯誤對象,表明存在問題。然后我們使用fromNodeCallback專門從Node.js樣式的回調(diào)中創(chuàng)建Observable:
var Rx = require("rx"); // Load RxJS var fs = require("fs"); // Load Node.js Filesystem module // Create an Observable from the readdir method var readdir = Rx.Observable.fromNodeCallback(fs.readdir); // Send a delayed message var source = readdir("/Users/sergi"); var subscription = source.subscribe( function(res) { console.log("List of directories: " + res);}, function(err) { console.log("Error: " + err); }, function() { console.log("Done!"); });
前面的代碼中,我們使用Node.js的fs.readdir方法創(chuàng)建一個Observable readdir。 fs.readdir接受目錄路徑和回調(diào)函數(shù)delayedMsg,該函數(shù)在檢索目錄內(nèi)容后調(diào)用。
我們使用readdir和我們傳遞給原始fs.readdir的相同參數(shù),省掉了回調(diào)函數(shù)。 這將返回一個Observable,當(dāng)我們訂閱一個Observer時,它將正確使用onNext,onError和onCompleted。
總結(jié)在本章中,我們探討了響應(yīng)式編程,并了解了RxJS如何通過Observable解決其他問題的方法,例如callback或promise。現(xiàn)在您了解為什么Observables功能強(qiáng)大,并且您知道如何創(chuàng)建它們。有了這個基礎(chǔ),我們現(xiàn)在可以繼續(xù)創(chuàng)建更有趣的響應(yīng)式程序。下一章將向您展示如何創(chuàng)建和組合基于序列的程序,這些程序為Web開發(fā)中的一些常見場景提供了更“可觀察”的方法。
關(guān)注我的微信公眾號,更多優(yōu)質(zhì)文章定時推送
創(chuàng)建了一個程序員交流微信群,大家進(jìn)群交流IT技術(shù)
如果已過期,可以添加博主微信號15706211347,拉你進(jìn)群
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/96821.html
摘要:由于技術(shù)棧的學(xué)習(xí),筆者需要在原來函數(shù)式編程知識的基礎(chǔ)上,學(xué)習(xí)的使用。筆者在社區(qū)發(fā)現(xiàn)了一個非常高質(zhì)量的響應(yīng)式編程系列教程共篇,從基礎(chǔ)概念到實際應(yīng)用講解的非常詳細(xì),有大量直觀的大理石圖來輔助理解流的處理,對培養(yǎng)響應(yīng)式編程的思維方式有很大幫助。 showImg(https://segmentfault.com/img/bVus8n); [TOC] 一. 響應(yīng)式編程 響應(yīng)式編程,也稱為流式編程...
摘要:響應(yīng)式編程第一章響應(yīng)式響應(yīng)式編程第二章序列的深入研究響應(yīng)式編程第三章構(gòu)建并發(fā)程序響應(yīng)式編程第四章構(gòu)建完整的應(yīng)用程序響應(yīng)式編程第五章使用管理時間響應(yīng)式編程第六章使用的響應(yīng)式應(yīng)用程序使用管理時間自從接觸,就開始在我的項目中使用它。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完...
摘要:本文是響應(yīng)式編程第一章響應(yīng)式這篇文章的學(xué)習(xí)筆記。通過代碼對比可以發(fā)現(xiàn),在響應(yīng)式編程中,我們不再用對象的概念來對現(xiàn)實世界進(jìn)行建模,而是使用流的思想對信息進(jìn)行拆分和聚合。 本文是Rxjs 響應(yīng)式編程-第一章:響應(yīng)式這篇文章的學(xué)習(xí)筆記。示例代碼地址:【示例代碼】 更多文章:【《大史住在大前端》博文集目錄】 showImg(https://segmentfault.com/img/bVbuE...
摘要:接下來,我們將實現(xiàn)一個真實的應(yīng)用程序,顯示幾乎實時發(fā)生的地震。得到的由表示,其中包含和的合并元素。如果不同同時傳出元素,合并序列中這些元素的順序是隨機(jī)的。是操作序列的強(qiáng)大操作符。但是的方法仍在運(yùn)行,表明取消并不會取消關(guān)聯(lián)的。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整...
摘要:從這個系列的第一章開始到第五章,基于的響應(yīng)式編程的基礎(chǔ)知識基本上就介紹完了,當(dāng)然有很多知識點沒有提到,比如,等,不是他們不重要,而是礙于時間精力等原因沒辦法一一詳細(xì)介紹。 從這個系列的第一章開始到第五章,基于rxjs的響應(yīng)式編程的基礎(chǔ)知識基本上就介紹完了,當(dāng)然有很多知識點沒有提到,比如 Scheduler, behaviorSubject,replaySubject等,不是他們不重要,...
閱讀 772·2021-09-28 09:35
閱讀 2601·2019-08-29 11:25
閱讀 2165·2019-08-23 18:36
閱讀 1864·2019-08-23 16:31
閱讀 2078·2019-08-23 14:50
閱讀 3131·2019-08-23 13:55
閱讀 3299·2019-08-23 12:49
閱讀 2091·2019-08-23 11:46