rxjs簡單入門
rxjs簡單入門
rxjs全名Reactive Extensions for JavaScript,Javascript的響應式擴充套件, 響應式的思路是把隨時間不斷變化的資料、狀態、事件等等轉成可被觀察的序列(Observable Sequence),然後訂閱序列中那些Observable物件的變化,一旦變化,就會執行事先安排好的各種轉換和操作
rxjs適用於非同步場景,即前端互動中介面請求、瀏覽器事件以及自定義事件。通過使用rxjs帶給我們前所未有的開發體驗。
- 統一非同步程式設計的規範,不管是Promise、ajax還是事件,通通封裝成
序列(Observable Sequence)
,一旦有非同步環節發生變更,觀察序列即可截獲發生變更的資訊。 - 前端業務層和展現層解耦,比如展現層不需要關係指定事件觸發時和DOM無關的處理邏輯。同時業務層也能組裝非同步操作中多個非同步邏輯之間的關係,無需暴露給展現層。展現層關心的是:非同步操作其中環節的資料變化。
- rxjs開發業務層具有高彈性,高穩定性,高實時性等特點。
廢話不多說,此篇文件結合模擬場景的例子,通過傻瓜式的描述來說明rxjs常用的方法以及組合關係。
1. Let`s Go
rxjs應用觀察者模式,其中包含2個重要的例項:Observer觀察者和Subject被觀察物件,多個Observer註冊到Subject中,在Subject功能觸發時,會通知註冊好的Observab列表,逐一通知其響應觀察變更資訊。
1.1 quick start
-
先從官網搬來rxjs的幾個例項概念
-
Observable
: 可觀察的資料序列. -
Observer
: 觀察者例項,用來決定何時觀察指定資料. -
Subscription
: 觀察資料序列返回訂閱例項. -
Operators
:Observable
的操作方法,包括轉換資料序列,過濾等,所有的Operators
方法接受的引數是上一次傳送的資料變更
的值,而方法返回值我們稱之為發射新資料變更
. -
Subject
: 被觀察物件. -
Schedulers
: 控制排程併發,即當Observable接受Subject的變更響應時,可以通過scheduler設定響應方式,目前內建的響應可以呼叫Object.keys(Rx.Subject)
檢視。
-
-
我們最常用也最關心的Observable,四個生命週期:建立 、訂閱 、 執行 、銷燬。
- 建立Obervable,返回被觀察的
序列源例項
,該例項不具備傳送資料的能力,相比之下通過new Rx.Subject
建立的觀察物件例項
具備傳送資料來源的能力。 - 通過
序列源例項
可以訂閱序列發射新資料變更時的響應方法(回撥方法) - 響應的動作實際上就是Observable的執行
- 通過
序列源例項
可以銷燬,而當訂閱方法發生錯誤時也會自動銷燬。 -
序列源例項
的catch
方法可以捕獲訂閱方法發生的錯誤,同時序列源例項
可以接受從catch
方法返回值,作為新的序列源例項
- 建立Obervable,返回被觀察的
-
掌握最簡單的例子
// 5.0.0-rc.1 import Rx from `rxjs`; //emit 1 from promise const source = Rx.Observable.fromPromise(new Promise(resolve => resolve(1))); //add 10 to the value const example = source.map(val => val + 10); //output: 11 const subscribe = example.subscribe(val => console.log(val));
通過程式碼掌握Observable
, Observer
, Subscription
, Operators
, Subject
和Schedulers
之間的關係
import Rx from `rxjs`;
/**
Rx.Observable是Observable
Rx.Observable.create建立序列源source,建立source的方法有多個,比如of, from, fromPromise等
observer是Observer觀察者,只有在Rx.Observable.create建立方法可以獲取,其他建立方法內建了observer且不可訪問
observer.next發射資料更新
source.map其中map就是Operators的其中一個方法,方法呼叫返回新的source1
source1.subscribe是訂閱,即資料更新時的響應方法。同時返回訂閱例項Subscription
subscription.next立即響應(不同於發射)靜態資料,此時不會經過`Operators`處理
! Rx.Observable.create或者Rx.Subject.create建立的source不會自動關閉,其他方式則當檢測到沒有序列發生變更會自動銷燬source.
*/
const source = Rx.Observable.create(observer => {
observer.next(`foo`);
setTimeout(() => observer.next(`bar`), 1000);
});
const source1 = source.map(val => `hello ${val}`);
const subscription = source1.subscribe(value => console.log(value));
subscription.next(`foo1`);
// forEach和subscribe相似,同是實現訂閱效果,等到promise可以監控subscription完成和失敗的異常。
// 日誌列印並沒有comlete, 因為source並沒有完成關閉,觸發呼叫observer.complete()
const promise = source1.forEach(value => console.log(value))
promise.then(() => console.log(`complete`), (err) => console.log(err));
/**
output:
hello foo
foo1
hello foo
hello bar
hello bar
*/
/**
new Subject建立被觀察者例項,同source一樣都具備subscribe方法,表示的含義和作用也一樣,即發射資料變更時響應方法。
subject.next立即發射資料變更,作用同observer.next
注意foo1是最後輸出的,是因為在建立source時指定了Rx.Scheduler.async,是非同步的排程器,表示在響應資料處理時是非同步執行的。
*/
Rx.Observable.of(`foo1`, Rx.Scheduler.async).subscribe(value => console.log(value));
const subject = new Subject();
const source2 = subject.map(val => `hello ${val}`);
const subscription = source1.subscribe(value => console.log(value));
subject.next(`foo`);
subscription.next(`bar`);
/**
output:
hello foo
bar
foo1
*/
1.2 學會看rxjs互動圖
互動圖中每條連表示一個資料序列,每個球表示每次發射的變更,最後一條線表示最終產出的資料序列。
下圖以combineLastest來舉例:
- 方法之上的每條線都是一個source(資料序列例項)
- 方法之下方法呼叫後返回的新source
- combineLastest表示被組合的每個source,一旦發射資料變更,必須拿到其餘的source的最新值(當非同步時則等待,直到都拿到最新值),組合為新的資料,作為新source發射的資料變更。
source1: ————————①——————————②——————————③————————————④—————————⑤——————————|——>
source2: ———————————ⓐ————————ⓑ————————————ⓒ—————————————————————ⓓ—————————|——>
combineLastest(source1, source2, (x, y) => x + y)
source: ———————(①ⓐ)—(②ⓐ)—(②ⓑ)—————(③ⓑ)—(③ⓒ)———(④ⓒ)————(⑤ⓒ)—(⑤ⓓ)——|——>
2. 例項方法Operators
前面講過Operators
方法呼叫時,接收的引數是source,返回新的source, 以下是個人學習使用過程中,簡單總結的rxjs各方法用法。
2.1 建立
- 發射完資料更新自動關閉:
from
,fromPromise
,of
,from
,range
- 不發射直接關閉:
empty
- 丟擲異常後關閉:
throw
- 不發射資料也不關閉:
never
- 保持發射資料且不自動關閉:
timer
,interval
,fromEvent
- 需要手動發射資料且不自動關閉:
create
, (還有Rx.Subject.create
)
2.2 轉換
-
1:1效果:
map
,mapTo
,flatMap
,scan
,expand
,pluck
-
map
,source = source1.map(func)表示source1每次發射資料時經過func函式處理,返回新的值作為source發射的資料 -
mapTo
,不同於map
,func改為靜態值 -
flatMap
,當發射的資料是一個source時,在訂閱的響應方法中接收到的也是一個source(這是合理的,發射什麼資料就響應什麼資料嘛,但是如果我們想在響應方法收到的是source的發射資料),flatMap就是可以允許發射資料是一個source,同時在響應的時候接收的是source的傳送資料,後面我們稱之為source打平 -
scan
,source = source1.scan(func, initialValue), source每次發射的資料是source前次發射資料和source1當前發射的資料 的組合結果(取決於func,一般是相加), initialValue第一次發射,source前次沒發射過,採用initialValue作為前次發射的資料 -
expand
,和scan
不同的是當func返回值是一個source時,在func接收到的資料是source打平
後的發射資料。特別適用於polling長輪詢 -
pluck
,每次發射資料時,獲取資料中的指定屬性的值作為source的發射資料
-
-
1:N效果:
concat
,concatAll
,concatMap
,concatMapTo
,merge
,mergeAll
,mergeMap
,mergeMapTo
,switchMap
,switchMapTo
-
concat
,concatAll
和merge
,mergeAll
屬於組合型別,放在這講更好體現其效果。 -
concat
,source = source1.concat(source2)表示source發射陣列的順序是,當source1或source2發射資料,source就發射。但是隻有當source1發射完且關閉(source1不在傳送資料)後,才觸發source2發射資料。 -
concatAll
,不同於concat
,會把所有的發射的資料打平(如果資料為source時),然後在決定下次發射哪個資料。 -
concatMap
,source = source1.concatMap(source2)表示source1每次發射資料時,獲取source2的所有發射資料,map返回多個待發射資料,按順序發射第一個資料變更。 -
concatMapTo
, 不同於concatMap
, map處理以source2的資料為返回結果 -
switchMap
, 和concatMap
不同的是在map之後的待發射資料排序上,concatMap
中source1每次發射時source2的所有發射資料都接收,作為source1下一次發射前,之間的所有發射資料。switchMap
則會判斷source2的所有發射資料是否有資料的發射時間比source1下一次發射的時間晚,找出來去除掉。 -
switchMapTo
對switchMap
就好比concatMap
對concatMapTo
,mergeMap
對比mergeMapTo
的關係也是如此。 -
mergeMap
相比於switchMap
,找出的資料會打平到source中,不丟棄。
-
-
N:1效果:
buffer
,bufferCount
,bufferTime
,bufferWhen
-
buffer
,source = source1.buffer(source2)表示source1以source2為參考,在source2的2次發射資料之間為時間段,source才發射一次資料,資料為該時間段內source1本該發射的資料的組合。 - 比如source1原先每隔1秒發射一次資料,source2是每個2秒發射資料,source = source1.buffer(source2), 那麼source會每隔2秒發射資料(source1的2秒內發射的2個數值組成的陣列)
-
bufferCount
,source = source1.bufferCount(count, start), count表示source1毎3次發射資料作為source的一次發射資料,發射完後,以source1當前組合的發射資料的第start個開始算下次發射資料需要組合的起始資料。 -
bufferTime
,一段時間內的source1發射資料作為source的一次發射資料 -
bufferWhen
, 以預設結果為準分成2段,分別作為source的每次發射資料
-
-
1:source效果:
groupBy
,window
,windowCount
,windowTime
,windowWhen
-
groupBy
, source = source1.groupBy(func), 表示source1的所有發射資料,按func分成多段,每段作為source的每次傳送的資料(這裡資料只是新的source,你可以理解為inner Observable例項) -
window
和buffer
不同的時,source每次傳送的是innerObservable -
window
vswindowCount
vswindowTime
vswindowWhen
同buffer
相似
-
-
1:sources效果:
partition
-
partition
,sources = source1.partition(func), 根據func吧所有的source1發射資料分段,每段組成一個source,最終得到sources陣列
-
2.3 過濾
source的過濾不會對發射資料做任何改變,只是減少source的發射次數,所以理解起來會簡單很多,這裡只做個簡單分類
- 防抖動(一段時間內只取最新資料作為一次發射資料,其他資料取消發射):
debounce
,debounceTime
,throttle
(和debounce
唯一區別是debounce
取一段時間內最新的,而throttle
忽略這段時間後,發現新值才傳送),throttleTime
- 去重(重疊的發射資料只去第一資料作為發射資料,其他相同資料取消發射):
distinct
,distinctUntilChanged
- 定位(根據條件值去一個或部分幾個資料作為對應發射資料,其他取消發射):
elementAt
,first
,last
,filter
,take
,takeLatst
,takeUntil
,takeWhile
, - 跳過(根據條件去除符合條件的,取剩下的值作為每次發射資料):
skip
,skipUntil
,skipWhile
,ignoreElements
(忽略所有的,等同於empty
) - 樣本:
sample
, source=source1.sample(source2), 以source2發射資料時來發現最新一次source1發射的資料,作為source的發射資料,個人覺得應該屬於轉換分類,官網放到了過濾
2.4 組合
做個source組合成新的souce
-
concat
,concatAll
和merge
,mergeAll
,在轉換分類講過了 -
combineLastest
,source = source1.combineLastest(source2, func),source1和source2一旦發射資料,func會觸發,拿到source1和source2最新的發射資料,返回新的資料,作為source的發射資料。 -
combineAll
,同combineLastest
,,source = sources.combineAll() -
forkJoin
,source = Rx.Observable.forkJoin(sources), 所有的sources都關閉後,獲取各自最新的發射陣列組合為陣列,作為source的發射資料 -
zip
和forkJoin
的區別是,zip
是sources都有傳送資料時,組合為一個陣列作為source的傳送資料,而sources任一source關閉了,則取source最後發射的數值。 -
zipAll
,同concat
對concatAll
-
startWith
,source = source1.startWith(value), 表示在source1的最前面注入第一次發射資料 -
withLastestFrom
, soruce = source1.withLastestFrom(source2, func), 表示source1每次發射資料時,獲取source2最新發射的資料,如果存在則func處理得到新的陣列作為source的發射資料
2.5 判斷
-
find
和findIndex
分別是指定發射資料和發射資料的下標(第幾次傳送的),應該放到過濾分類才合理 -
isEmpty
,every
,include
等,判斷是否為真,判斷的結果當做是source的發射資料
2.6 錯誤處理
-
catch
,source在Operators
呼叫過程中出現的異常,都可以在catch
捕獲到,同時可以返回新的source,因為出現異常的當前source會自動銷燬掉。 -
retry
,source = source.retry(times), source的所有發射,重複來幾遍。 -
retryWhen
,根據條件來決定來幾遍,只有當條件為false時才跳出迴圈。
2.7 工具
-
do
,在每次響應訂閱前,可以通過source.do(func),做一些提前處理等任何動作,比如列印一下發射的資料等。 -
delay
,delayWhen
,每次傳送資料時,都延遲一定時間間隔後再傳送。 -
observeOn
, 設定scheduler,即發射資料的響應方式,Schedulers詳細檢視地址, 這裡不講解了,專案中應用得不多。 -
subcribeOn
,timeInterval
設定sheduler -
toPromise
, source轉成promise,可以通過promise.then達到source.subscribe的效果 -
toArray
,把source所有發射的資料,組成陣列輸出。
2.8 計算
把source的所有發射資料進行指定計算後,得出的資料作為新source的發射資料,計算方法分別有:max
, min
, count
, reduce
, average
等
2.9 其他
-
cache
, source = source1.cache(1);共享source1的訂閱結果,即不管source訂閱幾回,響應方法接收到的發射資料都是同一份。 - 共享source訂閱結果很重要,因為組合等方法組合多個source時,其中包含sourceA,同時sourceA還需要單獨訂閱其結果,在不用
cache
情況下,sourceA會產生2個subscription,即2個訂閱例項,但是我們更希望是能達到sourceA發生變化時,都能通知到所有的組合sourceA的source。 -
publish
,publishSource = source.publish(),讓source的訂閱的工作延後,即source不會發射資料,而是等到publishSource.connect()呼叫後才開發發射資料。效果和delay
很相似,不同的是可以控制合適發射。 -
share
,當source訂閱多次,那麼每次響應時do
都會呼叫多次,通過share
合併響應,則source發射一次資料更新,多次響應當當一次響應處理,do
也呼叫一次。
參考資料
- rxjs官網 – http://reactivex.io/rxjs/
- rxjs程式碼 – https://github.com/ReactiveX/rxjs
- 常用rxjs方法的互動圖 – http://rxmarbles.com/
- rxhjs教程 – http://xgrommx.github.io/rx-book/content/observable/observable_instance_methods/toarray.html
- Scheduler – https://mcxiaoke.gitbooks.io/rxdocs/content/Scheduler.html
相關文章
- Rxjs建模入門JS
- Rxjs光速入門JS
- rxjs入門4之rxjs模式設計JS模式
- SpringSecurity簡單入門SpringGse
- git簡單入門Git
- SprintBoot簡單入門boot
- 簡單入門Kubernetes
- Mysql 簡單入門MySql
- Vue簡單入門Vue
- Azkaban 簡單入門
- postgresql 簡單入門SQL
- Kafka簡單入門Kafka
- rxjs入門之ajax封裝JS封裝
- Rust Rocket簡單入門Rust
- 入門Flink,很簡單
- Kubernetes Ingress簡單入門
- PWA超簡單入門
- Android 混淆簡單入門Android
- 小程式 – 簡單入門
- Quartz - Quartz簡單入門quartz
- nuxt簡單入門安裝UX
- drools的簡單入門案例
- 簡單的 Go 入門教程Go
- NoSLQ之MongoDB簡單入門MongoDB
- opengl簡單入門例項
- JDBC入門與簡單使用JDBC
- EChart.js簡單入門JS
- 貪心(入門簡單題)
- ASP入門教程 1小時ASP入門,非常簡單
- [2]SpinalHDL教程——Scala簡單入門
- Nginx 簡單入門指北不指南Nginx
- Spark Streaming簡單入門(示例+原理)Spark
- Spring Data JPA的簡單入門Spring
- CI 框架簡單入門筆記框架筆記
- ARouter簡單入門和介紹
- springmvc簡單學習(一)-----入門SpringMVC
- spring Cloud Gateway 入門簡單使用SpringCloudGateway
- Dubbo入門(2) – 簡單實踐
- Dubbo入門(2) - 簡單實踐