成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

Rxjs 響應(yīng)式編程-第一章:響應(yīng)式

songze / 1043人閱讀

摘要:響應(yīng)式編程具有很強(qiáng)的表現(xiàn)力,舉個(gè)例子來(lái)說(shuō),限制鼠標(biāo)重復(fù)點(diǎn)擊的例子。在響應(yīng)式編程中,我把鼠標(biāo)點(diǎn)擊事件作為一個(gè)我們可以查詢(xún)和操作的持續(xù)的流事件。這在響應(yīng)式編程中尤其重要,因?yàn)槲覀冸S著時(shí)間變換會(huì)產(chǎn)生很多狀態(tài)片段。迭代器模式的另一主要部分來(lá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管理時(shí)間
Rxjs 響應(yīng)式編程-第六章 使用Cycle.js的響應(yīng)式Web應(yīng)用程序

響應(yīng)式

現(xiàn)實(shí)世界相當(dāng)混亂:事件不按照順序發(fā)生,應(yīng)用崩潰,網(wǎng)絡(luò)不通。幾乎沒(méi)有應(yīng)用是完全同步的,所以我們不得不寫(xiě)一些異步代碼保持應(yīng)用的可響應(yīng)性。大多數(shù)的時(shí)候是很痛苦的,但也并不是不可避免。

現(xiàn)代應(yīng)用需要超級(jí)快速的響應(yīng)速度,并且希望能夠不漏掉一個(gè)字節(jié)的處理來(lái)自不同數(shù)據(jù)源的數(shù)據(jù)。然而并沒(méi)有現(xiàn)成的解決方案,因?yàn)樗鼈儾粫?huì)隨著我們添加并發(fā)和應(yīng)用程序狀態(tài)而擴(kuò)展代碼變得越來(lái)越復(fù)雜。

本章向您介紹反應(yīng)式編程,這是一種自然,簡(jiǎn)單的方法處理異步代碼的方式。我會(huì)告訴你事件的流程 - 我們稱(chēng)之為Observables - 是處理異步代碼的一種很好的方式。
然后我們將創(chuàng)建一個(gè)Observable,看看響應(yīng)式思維和RxJS是怎么樣改善現(xiàn)有技術(shù),讓你成為更快樂(lè),更多高效的程序員。

什么是響應(yīng)式?

讓我們從一個(gè)小的響應(yīng)性RxJS程序開(kāi)始。 這個(gè)程序需要通過(guò)單擊按鈕檢索來(lái)自不同來(lái)源的數(shù)據(jù),它具有以下要求:

它必須統(tǒng)一來(lái)自使用不同源的JSON結(jié)構(gòu)

最終結(jié)果不應(yīng)包含任何副本

為了避免多次請(qǐng)求數(shù)據(jù),用戶(hù)不能重復(fù)點(diǎn)擊按鈕

使用RxJS,我們的代碼類(lèi)似這樣:

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)注于成果即可。你看到的第一件事是我們使用更少的代碼實(shí)現(xiàn)更多的功能。我們通過(guò)使用Observable來(lái)實(shí)現(xiàn)這一目標(biāo)。

Observable表示數(shù)據(jù)流。程序也可以可以主要表示為數(shù)據(jù)流。在前面的示例中,兩個(gè)遠(yuǎn)程源是Observables,用戶(hù)點(diǎn)擊鼠標(biāo)也是如此。實(shí)際上,我們的程序本質(zhì)上是一個(gè)由按鈕的單擊事件構(gòu)成的Observable,我們把它轉(zhuǎn)變成獲得我們想要的結(jié)果。

響應(yīng)式編程具有很強(qiáng)的表現(xiàn)力,舉個(gè)例子來(lái)說(shuō),限制鼠標(biāo)重復(fù)點(diǎn)擊的例子。想象一下我們使用我們使用promise和callback實(shí)現(xiàn)這個(gè)功能是有多復(fù)雜:我們需要每秒重置一下點(diǎn)擊次數(shù),并且在用戶(hù)點(diǎn)擊之后每秒都要保存點(diǎn)擊狀態(tài)。但是這樣子,對(duì)于這個(gè)小功能來(lái)說(shuō)就顯得過(guò)于復(fù)雜了,并且所寫(xiě)代碼與業(yè)務(wù)功能并沒(méi)有直觀(guān)的聯(lián)系。為了彌補(bǔ)基礎(chǔ)代碼庫(kù)的功能不足,在一個(gè)大型應(yīng)用中,這些很小的復(fù)雜功能會(huì)增加的非??臁?/p>

通過(guò)響應(yīng)式編,我們使用debounce方法來(lái)限制點(diǎn)擊流次數(shù)。這樣就保證每次點(diǎn)擊的間隔時(shí)間至少1s,忽略1s之間的點(diǎn)擊次數(shù)。我們不關(guān)心內(nèi)部如何實(shí)現(xiàn),我們只是表達(dá)我們希望代碼執(zhí)行的操作,而不是如何操作。

這就變得更有趣了。接下來(lái),您將看到反應(yīng)式編程如何幫助我們提高課程效率和表現(xiàn)力。

電子表格是可響應(yīng)的

讓我們從這樣一個(gè)響應(yīng)性系統(tǒng)的典型例子開(kāi)始考慮:點(diǎn)子表格。我們都是使用過(guò)吧,但我們很少停下來(lái)思考它們是多么令人震驚的直觀(guān)。假設(shè)我們?cè)陔娮颖砀竦膯卧馎1中有一個(gè)值,然后我們可以在電子表格中的其他單元格中引用它,并且每當(dāng)我們更改A1時(shí),每個(gè)依賴(lài)于A1的單元格都會(huì)自動(dòng)更新與A1同步。

這些操作對(duì)我們感覺(jué)很自然,我們并不會(huì)去告訴計(jì)算機(jī)去根據(jù)A1更新單元格或者如何更新;這些單元格就自動(dòng)這樣子做了。在點(diǎn)子表格中,我們只需要簡(jiǎn)單的聲明我們需要處理的問(wèn)題,不用操心計(jì)算機(jī)如何處理。

鼠標(biāo)輸入作為streams

理解如何把事件作為流,我們回想一下本章開(kāi)頭的那個(gè)程序。在那里,我們使用鼠標(biāo)點(diǎn)擊作為用戶(hù)點(diǎn)擊時(shí)實(shí)時(shí)生成的無(wú)限事件流。這個(gè)想法起源于Erik Meijer,也就是Rxjs的作者。他認(rèn)為:你的鼠標(biāo)就是一個(gè)數(shù)據(jù)庫(kù)。

在響應(yīng)式編程中,我把鼠標(biāo)點(diǎn)擊事件作為一個(gè)我們可以查詢(xún)和操作的持續(xù)的流事件。想象成流而不是一個(gè)孤立的事件,這種想法開(kāi)辟了一種全新的思考方式。我們可以在其中操縱尚未創(chuàng)建的整個(gè)值的流。

好好想想。這種方式有別于我們以往的編程方式,之前我們把數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù),或者數(shù)組并且等待這些數(shù)據(jù)可用之后在使用它們。如果它們尚不可用(舉個(gè)例子:一個(gè)網(wǎng)絡(luò)請(qǐng)求),我們只能等它們好了才可以使用。

我們可以將流視為所在由時(shí)間而不是存儲(chǔ)位置分開(kāi)的數(shù)組。無(wú)論是時(shí)間還是存儲(chǔ)位,我們都有元素序列:

將您的程序視為流動(dòng)的數(shù)據(jù)序列是理解的RxJS程序的關(guān)鍵。這需要一些練習(xí),但并不難。事實(shí)上,大多數(shù)我們?cè)谌魏螒?yīng)用程序中使用的數(shù)據(jù)都可以表示為序列。

序列查詢(xún)

讓我們使用傳統(tǒng)javascript傳統(tǒng)的事件綁定技術(shù)來(lái)實(shí)現(xiàn)一個(gè)鼠標(biāo)點(diǎn)擊流。要記錄鼠標(biāo)點(diǎn)擊的x和y坐標(biāo),我們可以這樣寫(xiě):

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
...

看起來(lái)像一個(gè)序列,不是嗎? 當(dāng)然,問(wèn)題在于操縱事件并不像操縱數(shù)組那么容易。 例如,如果我們想要更改前面的代碼,使其僅記錄前10次位于屏幕右側(cè)的單擊事件(相當(dāng)隨機(jī)的目標(biāo)),我們會(huì)寫(xiě)像這樣的東西:

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);
    }
});

為了滿(mǎn)足我們的要求,我們通過(guò)引入一個(gè)全局變量作為擴(kuò)展?fàn)顟B(tài)來(lái)記錄當(dāng)前點(diǎn)擊數(shù)。 我們還需要使用嵌套的條件來(lái)檢查兩個(gè)不同的條件。當(dāng)我們完成時(shí),我們必須注銷(xiāo)事件,以免泄漏內(nèi)存。

副作用和外部狀態(tài)

如果一個(gè)動(dòng)作在其發(fā)生的范圍之外產(chǎn)生影響,我們稱(chēng)之為一方副作用。更改函數(shù)外部的變量,打印到控制臺(tái)或更新數(shù)據(jù)庫(kù)中的值,這些都是副作用。例如改變函數(shù)內(nèi)部的變量是安全的,但是如果該變量超出了我們函數(shù)的范圍,那么其他函數(shù)也可以改變它的值,這就意味著這個(gè)功能不再受控制,因?yàn)槟銦o(wú)法預(yù)測(cè)外部會(huì)對(duì)這個(gè)變量作何操作。所以我們需要跟蹤它,添加檢查以確保它的變化符合我們的預(yù)期。但是這樣子添加的代碼其實(shí)與我們程序無(wú)關(guān),確增加程序的復(fù)雜度也更容易出錯(cuò)。雖然副作用總是會(huì)有的,但是我們應(yīng)該努力減少。這在響應(yīng)式編程中尤其重要,因?yàn)槲覀冸S著時(shí)間變換會(huì)產(chǎn)生很多狀態(tài)片段。所以避免外部狀態(tài)和副作用是貫穿本書(shū)一條宗旨。

我們?cè)O(shè)法滿(mǎn)足了我們的簡(jiǎn)單要求,但是為了實(shí)現(xiàn)這樣一個(gè)簡(jiǎn)單的目標(biāo),最終得到了相當(dāng)復(fù)雜的代碼。對(duì)于首次查看它的開(kāi)發(fā)人員來(lái)說(shuō),不容易懂且維護(hù)代碼很困難。 更重要的是,因?yàn)槲覀內(nèi)匀恍枰4嫱獠孔菜晕覀兒苋菀自谖磥?lái)發(fā)展出玄妙的錯(cuò)誤。

在這種情況下我們想要的只是查詢(xún)點(diǎn)擊的“數(shù)據(jù)庫(kù)”。如果我們是使用關(guān)系數(shù)據(jù)庫(kù),我們使用聲明性語(yǔ)言SQL:

SELECT x, y FROM clicks LIMIT 10

如果我們將點(diǎn)擊事件流視為可以查詢(xún)和轉(zhuǎn)變的數(shù)據(jù)源,該怎么辦?畢竟,它與數(shù)據(jù)庫(kù)沒(méi)有什么不同,都是一個(gè)可以處理數(shù)據(jù)的東西。我們所需要的只是一個(gè)為我們提供抽象概念的數(shù)據(jù)類(lèi)型。

輸入RxJS及其Observable數(shù)據(jù)類(lèi)型:

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)建一個(gè)Observable的點(diǎn)擊事件,并過(guò)濾掉在點(diǎn)擊事件上發(fā)生屏幕左側(cè)的點(diǎn)擊。然后只在控制臺(tái)打印前10次點(diǎn)擊的坐標(biāo)。

注意即使您不熟悉代碼也很容易閱讀,也沒(méi)有必要?jiǎng)?chuàng)建外部變量來(lái)保持狀態(tài)。這樣使我們的代碼是自包含的,不容易產(chǎn)生bug。所以也就沒(méi)必要去清除你的狀態(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)大的原理。

觀(guān)察者和迭代者

要了解Observable的來(lái)源,我們需要查看他們的基礎(chǔ):Observer和Iterator軟件模式。在本節(jié)中我們將快速瀏覽它們,然后我們將看到Observables如何結(jié)合,簡(jiǎn)單而有力。

觀(guān)察者模式

對(duì)于軟件開(kāi)發(fā)人員來(lái)說(shuō),很難不聽(tīng)到Observables就想起觀(guān)察者模式。在其中我們有一個(gè)名為Producer的對(duì)象,內(nèi)部保留訂閱者的列表。當(dāng)Producer對(duì)象發(fā)生改變時(shí),訂閱者的update方法會(huì)被自動(dòng)調(diào)用。(在觀(guān)察者模式的大部分解釋中,這個(gè)實(shí)體被叫做Subject,為了避免大家和RxJs的自己Subject混淆,我們稱(chēng)它為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對(duì)象在實(shí)例的偵聽(tīng)器中保留一個(gè)動(dòng)態(tài)的Listener列表,每當(dāng)Producer更新的時(shí)候都會(huì)調(diào)用其notify方法。在下面的代碼中,我們創(chuàng)建了兩個(gè)對(duì)象來(lái)監(jiān)聽(tīng)
notifie,一個(gè)Producer的實(shí)例。

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)行這個(gè)程序的時(shí)候:

Listener 1 received: Hello there!
Listener 2 received: Hello there!

當(dāng)notifier更新內(nèi)部狀態(tài)的時(shí)候,listener1listener2都會(huì)被更新。這些都不需要我們?nèi)ゲ傩摹?/p>

我們的實(shí)現(xiàn)很簡(jiǎn)單,但它說(shuō)明了觀(guān)察者模式允許觀(guān)察者和監(jiān)聽(tīng)器解耦。

迭代器模式

Observable的另一主要部分來(lái)自Iterator模式。一個(gè)Iterator是一個(gè)為消費(fèi)者提供簡(jiǎn)單的遍象它內(nèi)容的方式,隱藏了消費(fèi)者內(nèi)部的實(shí)現(xiàn)。

Iterator接口很簡(jiǎn)單。它只需要兩個(gè)方法:next()來(lái)獲取序列中的下一個(gè)項(xiàng)目,以及hasNext()來(lái)檢查是否還有項(xiàng)目序列。

下面是我們?nèi)绾尉帉?xiě)一個(gè)對(duì)數(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

迭代器非常適合封裝任何類(lèi)型數(shù)據(jù)結(jié)構(gòu)的遍歷邏輯。 正如我們?cè)谇懊娴睦又锌吹降哪菢?,迭代器在處理不同?lèi)型的數(shù)據(jù)的時(shí)候就會(huì)變得很有趣,或者在運(yùn)行的時(shí)候做配置,就像我們?cè)趲в衐ivisor參數(shù)的示例中所做的那樣。

Rx模式和Observable

雖然Observer和Iterator模式本身就很強(qiáng)大,但是兩者的結(jié)合甚至更好。 我們稱(chēng)之為Rx模式,命名為
在Reactive Extensions庫(kù)之后。我們將在本書(shū)的其余部分使用這種模式。

Observable序列或簡(jiǎn)單的Observable是Rx模式的核心。Observable按順序傳遞出來(lái)它的值 - 就像迭代器一樣 - 而不是消費(fèi)者要求它傳出來(lái)的值。這個(gè)和觀(guān)察者模式有相同之處:得到數(shù)據(jù)并將它們推送到監(jiān)聽(tīng)器。

pull和push

在編程中,基于推送的行為意味著應(yīng)用程序的服務(wù)器組件向其客戶(hù)端發(fā)送更新,而不是客戶(hù)端必須輪詢(xún)服務(wù)器以獲取這些更新。這就像是說(shuō)“不要打電話(huà)給我們; 我們會(huì)打電話(huà)給你。“
RxJS是基于推送的,因此事件源(Observable)將推動(dòng)新值給消費(fèi)者(觀(guān)察者),消費(fèi)者卻不能去主動(dòng)請(qǐng)求新值。

更簡(jiǎn)單地說(shuō),Observable是一個(gè)隨著時(shí)間的推移可以使用其數(shù)據(jù)的序列。Observables,也就是Observers的消費(fèi)者相當(dāng)于觀(guān)察者模式中的監(jiān)聽(tīng)器。當(dāng)Observe訂閱一個(gè)Observable時(shí),它將在序列中接收到它們可用的值,而不必主動(dòng)請(qǐng)求它們。

到目前為止,似乎與傳統(tǒng)觀(guān)察者沒(méi)有太大區(qū)別。 但實(shí)際上有兩個(gè)本質(zhì)區(qū)別:

Observable在至少有一個(gè)Observer訂閱它之前不會(huì)啟動(dòng)。

與迭代器一樣,Observable可以在序列完成時(shí)發(fā)出信號(hào)。

使用Observables,我們可以聲明如何對(duì)它們發(fā)出的元素序列做出反應(yīng),而不是對(duì)單個(gè)項(xiàng)目做出反應(yīng)。我們可以有效地復(fù)制,轉(zhuǎn)換和查詢(xún)序列,這些操作將應(yīng)用于序列的所有元素。

創(chuàng)建Observables

有幾種方法可以創(chuàng)建Observable,創(chuàng)建函數(shù)是最明顯的一種。 Rx.Observable對(duì)象中的create方法接受一個(gè)Observer參數(shù)的回調(diào)。 該函數(shù)定義了Observable將如何傳出值。這是我們?nèi)绾蝿?chuàng)建一個(gè)簡(jiǎn)單的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時(shí),它通過(guò)在其偵聽(tīng)器上調(diào)用onNext方法來(lái)發(fā)出三個(gè)字符串。 然后它調(diào)用onCompleted來(lái)表示序列已完成。 但是我們究竟如何訂閱Observable呢?我們使用Observers來(lái)做這件事情。

第一次接觸Observers

Observers監(jiān)聽(tīng)Observables。每當(dāng)Observable中觸發(fā)一個(gè)事件,它都會(huì)在所有Observers中調(diào)用相關(guān)的方法。

Observers有三種方法:onNextonCompletedonError

onNext????相當(dāng)于觀(guān)察者模式中的update。 當(dāng)Observable發(fā)出新值時(shí)調(diào)用它。請(qǐng)注意該名稱(chēng)如何反映我們訂閱序列的事實(shí),而不僅僅是離散值。

onCompleted????表示沒(méi)有更多可用數(shù)據(jù)。 調(diào)用onCompleted后,對(duì)onNext的進(jìn)一步調(diào)用將不起作用。

onError????在Observable中發(fā)生錯(cuò)誤時(shí)調(diào)用。 在調(diào)用之后,對(duì)onNext的進(jìn)一步調(diào)用將不起作用

以下是我們創(chuàng)建基本觀(guān)察者的方法:

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對(duì)象中的create方法接受onNext,onCompleted和onError情況的函數(shù),并返回一個(gè)Observer實(shí)例。這三個(gè)函數(shù)是可選的,您可以決定要包含哪些函數(shù)。例如,如果我們訂閱無(wú)限序列(例如點(diǎn)擊按鈕(用戶(hù)可以永久點(diǎn)擊)),則永遠(yuǎn)不會(huì)調(diào)用onCompleted處理程序。 如果我們確信序列不能出錯(cuò)(例如,通過(guò)從數(shù)組中生成一個(gè)Observable),我們就不需要onError方法了。

使用Observable進(jìn)行Ajax調(diào)用

我們還沒(méi)有對(duì)Observables做過(guò)任何實(shí)用的事情。如何創(chuàng)建一個(gè)檢索遠(yuǎn)程內(nèi)容的Observable?為此,我們將使用Rx.Observable.create包裝XMLHttpRequest對(duì)象。

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來(lái)包裝XMLHttpRequest。如果HTTP GET請(qǐng)求成功,我們emit它的內(nèi)容并結(jié)束序列(我們的Observable只會(huì)發(fā)出一個(gè)結(jié)果)。 否則,我們會(huì)emit一個(gè)錯(cuò)誤。在最后一行,我們傳入一個(gè)url進(jìn)行調(diào)用。 這將創(chuàng)建Observable,但它不會(huì)發(fā)出任何請(qǐng)求。這很重要:Observable在至少有一個(gè)觀(guān)察者描述它們之前不會(huì)做任何事情。 所以讓我們要這樣子做:

// 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"); }
);

首先要注意的是,我們沒(méi)有像之前的代碼那樣顯式創(chuàng)建Observer。大多數(shù)時(shí)候我們都會(huì)使用這個(gè)更短的版本,我們?cè)贠bservable中使用這三個(gè)訂閱Observer案例的函數(shù):next,completed和error。

subscribe然后一切就緒。在subscribe之前,我們只是聲明了Observable和Observer將如何交互。只有當(dāng)我們調(diào)用subscribe方法時(shí),一切才開(kāi)始運(yùn)行。

始終會(huì)有一個(gè)Operator

在RxJS中,轉(zhuǎn)換或查詢(xún)序列的方法稱(chēng)為Operator。Operator位于靜態(tài)Rx.Observable對(duì)象和Observable實(shí)例中。在我們的示例中,create就是一個(gè)這樣的Operator。

當(dāng)我們必須創(chuàng)建一個(gè)非常具體的Observable時(shí),create是一個(gè)很好的選擇,但是RxJS提供了許多其他Operator,可以很容易地為常用源創(chuàng)建Observable。

讓我們?cè)倏纯辞懊娴睦印?duì)于像Ajax請(qǐng)求這樣的常見(jiàn)操作,通常有一個(gè)Operator可供我們使用。 在這種情況下,RxJS DOM庫(kù)提供了幾種從DOM相關(guān)源創(chuàng)建Observable的方法。由于我們正在執(zhí)行GET請(qǐng)求,我們可以使用Rx.DOM.Request.get,然后我們的代碼就變成了這個(gè):

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)存在了。另請(qǐng)注意,這次我們省略了onCompleted回調(diào),因?yàn)槲覀儾淮蛩阍贠bservable complete時(shí)做出反應(yīng)。我們知道它只會(huì)產(chǎn)生一個(gè)結(jié)果,我們已經(jīng)在onNext回調(diào)中使用它了。

在本書(shū)中我們將使用這樣的大量便利操作符。這都是基于rxjs本身的能量,這也正式rxjs強(qiáng)大的地方之一。

一種可以約束全部的數(shù)據(jù)類(lèi)型

在RxJS程序中,我們應(yīng)該努力將所有數(shù)據(jù)都放在Observables中,而不僅僅是來(lái)自異步源的數(shù)據(jù)。 這樣做可以很容易地組合來(lái)自不同來(lái)源的數(shù)據(jù),例如現(xiàn)有數(shù)組與回調(diào)結(jié)果,或者XMLHttpRequest的結(jié)果與用戶(hù)觸發(fā)的某些事件。

例如,如果我們有一個(gè)數(shù)組,其項(xiàng)目需要與來(lái)自其他地方的數(shù)據(jù)結(jié)合使用,最好將此數(shù)組轉(zhuǎn)換為Observable。(顯然,如果數(shù)組只是一個(gè)不需要組合的中間變量,則沒(méi)有必要這樣做。)在本書(shū)中,您將了解在哪些情況下值得將數(shù)據(jù)類(lèi)型轉(zhuǎn)換為Observables。

RxJS為operators提供了從大多數(shù)JavaScript數(shù)據(jù)類(lèi)型創(chuàng)建Observable的功能。 讓我們回顧一下你將一直使用的最常見(jiàn)的:數(shù)組,事件和回調(diào)。

從數(shù)組創(chuàng)建Observable

我們可以使用通用的operators將任何類(lèi)似數(shù)組或可迭代的對(duì)象轉(zhuǎn)換為Observable。 from將數(shù)組作為參數(shù)并返回一個(gè)包含他所有元素的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)我們將一個(gè)事件轉(zhuǎn)換為一個(gè)Observable時(shí),它就變成了一個(gè)可以組合和傳遞的第一類(lèi)值。 例如,這是一個(gè)Observable,只要它移動(dòng)就會(huì)傳初鼠標(biāo)指針的坐標(biāo)。

var allMoves = Rx.Observable.fromEvent(document, "mousemove")

allMoves.subscribe(function(e) {
  console.log(e.clientX, e.clientY);
});

將事件轉(zhuǎn)換為Observable會(huì)將事件從之前的事件邏輯中釋放出來(lái)。更重要的是,我們可以基于原始的Observables創(chuàng)建新的Observable。這些新的是獨(dú)立的,可用于不同的任務(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)建了兩個(gè)Observable。 這些專(zhuān)門(mén)的Observable只包含原始的過(guò)濾項(xiàng):movesOnTheRight包含發(fā)生在屏幕右側(cè)的鼠標(biāo)事件,movesOnTheLeft包含發(fā)生在左側(cè)的鼠標(biāo)事件。 它們都沒(méi)有修改原始的Observable:allMoves將繼續(xù)發(fā)出所有鼠標(biāo)移動(dòng)。 Observable是不可變的,每個(gè)應(yīng)用于它們的operator都會(huì)創(chuàng)建一個(gè)新的Observable。

從回調(diào)函數(shù)創(chuàng)建Observable

如果您使用第三方JavaScript庫(kù),則可能需要與基于回調(diào)的代碼進(jìn)行交互。 我們可以使用fromCallbackfromNodeCallback兩個(gè)函數(shù)將回調(diào)轉(zhuǎn)換為Observable。Node.js遵循的是在回調(diào)函數(shù)的第一個(gè)參數(shù)傳入錯(cuò)誤對(duì)象,表明存在問(wèn)題。然后我們使用fromNodeCallback專(zhuān)門(mén)從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)建一個(gè)Observable readdir。 fs.readdir接受目錄路徑和回調(diào)函數(shù)delayedMsg,該函數(shù)在檢索目錄內(nèi)容后調(diào)用。

我們使用readdir和我們傳遞給原始fs.readdir的相同參數(shù),省掉了回調(diào)函數(shù)。 這將返回一個(gè)Observable,當(dāng)我們訂閱一個(gè)Observer時(shí),它將正確使用onNext,onError和onCompleted。

總結(jié)

在本章中,我們探討了響應(yīng)式編程,并了解了RxJS如何通過(guò)Observable解決其他問(wèn)題的方法,例如callback或promise?,F(xiàn)在您了解為什么Observables功能強(qiáng)大,并且您知道如何創(chuàng)建它們。有了這個(gè)基礎(chǔ),我們現(xiàn)在可以繼續(xù)創(chuàng)建更有趣的響應(yīng)式程序。下一章將向您展示如何創(chuàng)建和組合基于序列的程序,這些程序?yàn)閃eb開(kāi)發(fā)中的一些常見(jiàn)場(chǎng)景提供了更“可觀(guān)察”的方法。

關(guān)注我的微信公眾號(hào),更多優(yōu)質(zhì)文章定時(shí)推送

創(chuàng)建了一個(gè)程序員交流微信群,大家進(jìn)群交流IT技術(shù)

如果已過(guò)期,可以添加博主微信號(hào)15706211347,拉你進(jìn)群

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/96821.html

相關(guān)文章

  • 響應(yīng)編程的思維藝術(shù)】 (1)Rxjs專(zhuān)題學(xué)習(xí)計(jì)劃

    摘要:由于技術(shù)棧的學(xué)習(xí),筆者需要在原來(lái)函數(shù)式編程知識(shí)的基礎(chǔ)上,學(xué)習(xí)的使用。筆者在社區(qū)發(fā)現(xiàn)了一個(gè)非常高質(zhì)量的響應(yīng)式編程系列教程共篇,從基礎(chǔ)概念到實(shí)際應(yīng)用講解的非常詳細(xì),有大量直觀(guān)的大理石圖來(lái)輔助理解流的處理,對(duì)培養(yǎng)響應(yīng)式編程的思維方式有很大幫助。 showImg(https://segmentfault.com/img/bVus8n); [TOC] 一. 響應(yīng)式編程 響應(yīng)式編程,也稱(chēng)為流式編程...

    lscho 評(píng)論0 收藏0
  • Rxjs 響應(yīng)編程-第五章 使用Schedulers管理時(shí)間

    摘要:響應(yīng)式編程第一章響應(yīng)式響應(yīng)式編程第二章序列的深入研究響應(yīng)式編程第三章構(gòu)建并發(fā)程序響應(yīng)式編程第四章構(gòu)建完整的應(yīng)用程序響應(yīng)式編程第五章使用管理時(shí)間響應(yīng)式編程第六章使用的響應(yīng)式應(yīng)用程序使用管理時(shí)間自從接觸,就開(kāi)始在我的項(xiàng)目中使用它。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完...

    qingshanli1988 評(píng)論0 收藏0
  • 響應(yīng)編程的思維藝術(shù)】 (2)響應(yīng)Vs面向?qū)ο?/b>

    摘要:本文是響應(yīng)式編程第一章響應(yīng)式這篇文章的學(xué)習(xí)筆記。通過(guò)代碼對(duì)比可以發(fā)現(xiàn),在響應(yīng)式編程中,我們不再用對(duì)象的概念來(lái)對(duì)現(xiàn)實(shí)世界進(jìn)行建模,而是使用流的思想對(duì)信息進(jìn)行拆分和聚合。 本文是Rxjs 響應(yīng)式編程-第一章:響應(yīng)式這篇文章的學(xué)習(xí)筆記。示例代碼地址:【示例代碼】 更多文章:【《大史住在大前端》博文集目錄】 showImg(https://segmentfault.com/img/bVbuE...

    Tonny 評(píng)論0 收藏0
  • Rxjs 響應(yīng)編程-第二章:序列的深入研究

    摘要:接下來(lái),我們將實(shí)現(xiàn)一個(gè)真實(shí)的應(yīng)用程序,顯示幾乎實(shí)時(shí)發(fā)生的地震。得到的由表示,其中包含和的合并元素。如果不同同時(shí)傳出元素,合并序列中這些元素的順序是隨機(jī)的。是操作序列的強(qiáng)大操作符。但是的方法仍在運(yùn)行,表明取消并不會(huì)取消關(guān)聯(lián)的。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整...

    姘擱『 評(píng)論0 收藏0
  • 從命令響應(yīng)(六)

    摘要:從這個(gè)系列的第一章開(kāi)始到第五章,基于的響應(yīng)式編程的基礎(chǔ)知識(shí)基本上就介紹完了,當(dāng)然有很多知識(shí)點(diǎn)沒(méi)有提到,比如,等,不是他們不重要,而是礙于時(shí)間精力等原因沒(méi)辦法一一詳細(xì)介紹。 從這個(gè)系列的第一章開(kāi)始到第五章,基于rxjs的響應(yīng)式編程的基礎(chǔ)知識(shí)基本上就介紹完了,當(dāng)然有很多知識(shí)點(diǎn)沒(méi)有提到,比如 Scheduler, behaviorSubject,replaySubject等,不是他們不重要,...

    sanyang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<