原文:https://blog.csdn.net/qq_34414916/article/details/85194098node
Observablepromise
在開始講服務以前,咱們先來看一下一個新東西——Observable(可觀察對象),是屬於RxJS庫裏面的一個對象,能夠用來處理異步事件,例如HTTP請求(實際上,在Angular中,全部的HTTP請求返回的都是Observable),或許,你之前接觸過一個叫promise的東西,它們本質上面是相同的:都是生產者主動向消費者「push」產品,而消費者是被動接收的,可是他們二者仍是有很大區別的:Observable能夠發送任意多值,而且,在被訂閱以前,它是不會執行的!這是promise不具有的特色,下面砸門來詳細瞭解一下Observable異步
心法篇
Observable用於在發送方和接收方之間傳輸消息,爲了更好地理解,你能夠將這些消息當作是流
在建立Observable對象時,須要傳入一個函數做爲構造函數的參數,這個函數叫訂閱者函數,這個函數也就是生產者向消費者推送消息的地方
在被消費者subscribe(訂閱)以前,訂閱者函數不會被執行,直到subscribe()函數被調用,該函數返回一個subscription對象,裏面有一個unsubscribe()函數,消費者能夠隨時拒絕消息的接收!
subscribe()函數接收一個observer(觀察者)對象做爲入參
消息的發送能夠是同步的,也能夠是異步的
咱們可使用一系列的RxJS操做符,在這些消息被接收方接收以前,對它們進行一系列的處理、轉換,由於這些操做符都是純函數,沒有反作用,能夠放心使用,並不會產生指望以外的結果!
詳細教程篇
一、observer(觀察者)函數
有了可觀察對象(發送方),怎麼少得了observer(觀察者)來觀察可觀察對象呢!observer是一個對象,其中包含三個屬性:next,error,complete,它們都是函數spa
next:以接收的值做爲入參,在正常狀況下執行,可選
error:出錯的狀況下執行,可選
complete:傳輸完成的狀況下執行,可選
固然,這都是由你本身代碼決定的,(請繼續往下閱讀你就會明白).net
二、初識Observable命令行
咱們先經過RxJS庫中的(of)方法來建立一個Observable,而不是經過構造函數來建立(下一小節的內容)3d
在vscode中新建項目(文件夾),取名testRxJS,在該目錄下新建一個.ts文件,取名teaching1.0.ts,內容以下調試
import { of } from "rxjs"; function f1() { let observable = of(1, 2, 3); observable.subscribe({ next: num => console.log(num) }); } f1();
切換到vscode的命令行(ctrl+~),cd到本項目所在的文件夾,輸入tsc .\teaching1.0.ts,而後輸入node .\teaching.1.0.js,能夠看到控制檯有如下輸出code
代碼解析
(注意,若是上面關於怎樣運行ts程序,或者vscode操做你不熟悉的話,請先閱讀vscode中使用TypeScript,以及vscode一些經常使用的快捷鍵,特別是箭頭函數,若是你不熟悉的話,後面的內容將沒法進行!)
RxJS中的of方法用於建立一個Observable,它會將其參數一個一個的發送給接收方,正如這裏所看到的,它將1,2,3分別發送
subscribe()函數中接受一個observer對象,但這裏,只定義了next方法,能夠發現next方法接受一個參數,而這個參數就是生產者發送過來的值!而後將其打印在控制檯上,(至於爲何這個值就是生產者發送的值,請繼續閱讀,其實說白了,是由你代碼決定的,在下一小節,咱們將會使用Observable的構造函數來定義一個Observable,並本身定義訂閱者函數)
三、訂閱者函數
上面說了不少這樣的句子:請繼續閱讀,那麼,在這一小節,我將揭曉謎底
同目錄下新建一個teaching1.1.ts文件(注意,這篇博客牽涉的全部代碼都在該目錄(testRxJS)下,後面就很少講了),內容以下
function f2() { const observable = Observable.create(observer => { try { observer.next(1); observer.next(2); observer.next(3); } catch (e) { observer.error(e); } observer.complete(); }); const observer = { next: num => console.log(num), error: e => console.log(e), complete: () => console.log('complete!!!') } observable.subscribe(observer); } f2();
用上面一樣的方式運行,結果以下
能夠發現,這運行效果同用of建立的Observable是同樣的,只不過這裏我還爲observer定義了complete方法,因此它多輸出了「complete!!!」。或許你也猜到了,這段代碼就是of的內幕!!(注意,確定不是of的源代碼啊!),並且,若是出了錯,就會被catch到,從而執行observer的error方法,經過這一小節,是否是對Observable有了更清晰的認識——原來都是有你的代碼決定的,沒有想象中的那麼神祕
四、subscribe()(訂閱)
在被消費者subscribe(訂閱)以前,訂閱者函數不會被執行,直到subscribe()函數被調用,該函數返回一個subscription對象,裏面有一個unsubscribe()函數,消費者能夠隨時拒絕消息的接收!也就是說:在Observable調用subscribe函數以前,什麼也不會發生,就像下面這段代碼,控制檯什麼輸出內容都沒有
function f1() { let observable = of(1, 2, 3); } f1();
直到你訂閱這個observable對象,像下面這樣
function f1() { let observable = of(1, 2, 3); observable.subscribe({ next: num => console.log(num) }); } f1();
五、異步發送消息
其實,能夠發現,上面一下節生產者發送消息的方式是同步的!這一小節,咱們來個異步發送消息(等待2秒,再向消費者發送數字4),代碼以下
function f2() { const observable = Observable.create(observer => { try { let time = 0; observer.next(1); observer.next(2); observer.next(3); const intervalId = setInterval(() => { console.log(`wait ${++time}s`); }, 900) setTimeout(() => { observer.next(4); clearInterval(intervalId) }, 2000); } catch (e) { observer.error(e); } // observer.complete(); // 注意不能當即調用complete函數,否則會終止消息傳輸 setTimeout(() => observer.complete(), 3000) }); const observer = { next: num => console.log(num), error: e => console.log(e), complete: () => console.log('complete!!!') } observable.subscribe(observer); } f2();
運行結果以下
代碼解析
這裏的代碼可能有點複雜,但也是有基本元素組成:定時器、箭頭函數,只要瞭解這兩個知識點,這段代碼應該就沒有難度,但須要注意下面幾點
complete不能當即調用,由於數字4的傳輸實在2秒後,若是你當即調用complete函數,就會中斷傳輸,數字4也就接收不到了
注意到setInterval中的時間間隔並非1000ms,我把它寫成了900ms,多是一些微小的時間延遲的緣由,寫成1000ms並不會產生指望的結果
最後產生的結果就是:數字4被異步發送了!!!
五、初識unsubscribe()函數
在這一小節,咱們直接調用unsubscribe()函數,只爲看看效果,下一小節,咱們將本身寫一個subscribe函數,並返回一個包含unsubscribe函數的對象,用於中斷信息傳輸
function f3() { const obs = Observable.create(observer => { observer.next(1); setTimeout(() => observer.next(2), 2000); // 等待兩秒才發送下一個值 }); let suber = obs.subscribe({ next: x => console.log("接收到:", x), }); setTimeout(() => suber.unsubscribe(), 1000); // 在一秒後取消訂閱 } f3();
運行結果以下
能夠發現,接收方只接收到了數字1,
代碼解析
由於數字2 在兩秒鐘後纔會被髮送,而消費者在1秒鐘後便中斷了消息傳輸,因此,控制檯上面只打印了數字1
六、自定義subscribe()
看到這裏,我想你應該也有些想法了,前面說過,Observable的subscribe函數返回一個subscription對象,其中有一個unsubscribe函數,用於取消訂閱,你應該對subscribe函數的內部實現有着不少想法,如今,咱們試着本身寫一個!(在這裏,咱們就不須要Observable對象了,咱們使用函數調用來模擬訂閱),代碼以下
function f4() { function subscribe(observer) { var intervalID = setInterval(() => { observer.next('launch.....'); }, 1000); return { unsubscribe: () => clearInterval(intervalID) } } var subscription = subscribe({ next: (x) => console.log(x) }); setTimeout(() => subscription.unsubscribe(), 5000); } f4();
運行結果以下
代碼解析
這段代碼的意思是:每隔1秒向控制檯打印「launch....」,在第5秒的時候,取消打印(模擬取消訂閱的過程)
能夠發現,這個subscribe函數返回了一個對象!該對象中有一個unsubscribe函數!
經過這段代碼,咱們就模擬了Observable的subscribe函數的內部實現(僅僅是模擬而已)
七、使用RxJS中的操做符
在心法篇的時候,我就講過這些操做符的重要做用以及優勢,實際上,在開發實際應用的時候,你離不開它,在這裏,我只舉幾個簡單的例子,大概瞭解一下他們的用法就好
使用單個操做符
在這裏,以map這個操做符爲例,它能夠接收可觀察對象發送的值,並將其轉換另外的形式,並以一個可觀察對象發送這些新值,如今,咱們來寫一段有趣的代碼:將原來的可觀察對象發送的值所有轉換爲「hello world」,並訂閱這個操做符返回的新的可觀察對象,並輸出值,代碼以下
function f5() { const observable = of(1, 2, 3); const opt = map(num => 'hello world'); const newObservable = opt(observable); newObservable.subscribe(data => console.log(data)); } f5();
運行結果以下
能夠發現map將1,2,3轉換成了三個hello world並輸出在控制檯上,你也能夠發現,這種方式代碼結構並非很清晰(特別是在有多個操做符的狀況下),實際上,並不推薦這種寫法,下面咱們來看看另一種更好的寫法
使用多個操做符
咱們可使用管道(pipe),將多個操做符連接起來,並將操做符返回的結果組合成一個,這樣,代碼結構更清晰,在這裏,我將把map、tap這兩個操做符連接起來,使得,你既能夠看到原可觀察對象發送的值,你也能夠看到轉換後的結果
tip:tap操做符,能夠理解爲窺探,就是「不打擾」可觀察對象發送的值,可是又能夠取得這些值進行處理,常見的用法就是——調試功能
function f6() {
const observable = of(1, 2, 3);
const newObservable = observable.pipe(
tap(num => console.log(num)),
map(num => 'hello world')
);
newObservable.subscribe(data => console.log(data));
}
f6();
運行結果以下
這些寫代碼是否是很清晰呀!