一 什么是rxjs?
RxJS(Reactive Extensions for JavaScript)是一個(gè)用于響應(yīng)式編程的 JavaScript 庫。它通過使用可觀察對(duì)象(Observables)和操作符(Operators)來處理異步和事件驅(qū)動(dòng)的代碼。
什么是響應(yīng)式編程? 程序的輸入可以被當(dāng)成一個(gè)數(shù)據(jù)流,例如 excel表格的累加。
響應(yīng)式編程世界里知名度最高的框架 - Reactive Extension , 簡稱 RX,指實(shí)踐響應(yīng)式編程的一套工具。在Rx官網(wǎng)有這樣一段文字
An API for asynchronous programming with observable streams.
[圖片]
Rx的概念最初由微軟公司實(shí)現(xiàn)并開源,也就是Rx.NET,因?yàn)镽x帶來的編程方式大大改進(jìn)了異步編程模型,在.NET之后,眾多開發(fā)者在其他平臺(tái)和語言上也實(shí)現(xiàn)了Rx的類庫。可見,Rx其實(shí)是一個(gè)大家族,在這個(gè)大家族中,還有用Java實(shí)現(xiàn)的RxJava,用C++實(shí)現(xiàn)的RxCpp,用Ruby實(shí)現(xiàn)的Rx.rb,用Python實(shí)現(xiàn)的RxPy,當(dāng)然,還有這個(gè)大家族中最年長的Rx.NET。在本書中,我們介紹的是RxJS,也就是Rx的JavaScript語言實(shí)現(xiàn)。
關(guān)于設(shè)計(jì)模式:
任何一種模式,指的都是解決某一個(gè)特定類型問題的套路和方法?,F(xiàn)實(shí)世界的問題復(fù)雜多變,往往不是靠單獨(dú)一種模式能夠解決的,更需要的是多種模式的組合,RxJS的Observable就是觀察者模式和迭代器模式的組合。
觀察者模式 要解決的問題,就是在一個(gè)持續(xù)產(chǎn)生事件的系統(tǒng)中,如何分割功能,讓不同模塊只需要處理一部分邏輯,這種分而治之的思想是基本的系統(tǒng)設(shè)計(jì)概念,當(dāng)然,“分”很容易,關(guān)鍵是如何“治”。觀察者模式對(duì)“治”這個(gè)問題提的解決方法是這樣,將邏輯分為發(fā)布者(Publisher)和觀察者(Observer),其中發(fā)布者只管負(fù)責(zé)產(chǎn)生事件,它會(huì)通知所有注冊(cè)掛上號(hào)的觀察者,而不關(guān)心這些觀察者如何處理這些事件,相對(duì)的,觀察者可以被注冊(cè)上某個(gè)發(fā)布者,只管接收到事件之后就處理,而不關(guān)心這些數(shù)據(jù)是如何產(chǎn)生的。
迭代器模式(Iterator,也稱為“迭代器”)指的是能夠遍歷一個(gè)數(shù)據(jù)集合的對(duì)象,因?yàn)閿?shù)據(jù)集合的實(shí)現(xiàn)方式很多,可以是一個(gè)數(shù)組,也可以是一個(gè)樹形結(jié)構(gòu),也可以是一個(gè)單向鏈表……迭代器的作用就是提供一個(gè)通用的接口,讓使用者完全不用關(guān)心這個(gè)數(shù)據(jù)集合的具體實(shí)現(xiàn)方式。迭代器另一個(gè)容易理解的名字叫游標(biāo)(cursor),就像是一個(gè)移動(dòng)的指針一樣,從集合中一個(gè)元素移到另一個(gè)元素,完成對(duì)整個(gè)集合的遍歷。
- Rxjs 特點(diǎn)
- 使用數(shù)據(jù)流對(duì)現(xiàn)實(shí)問題進(jìn)行抽象
- 擅長處理異步操作
- 把復(fù)雜問題抽象成簡單問題的組合
- Rxjs 的基本概念
- 數(shù)據(jù)源 - 可觀察對(duì)象 Observable
import { Observable } from 'rxjs';
//代表“流”的變量標(biāo)示符,都是用$符號(hào)結(jié)尾,這是RxJS編程中普遍使用的風(fēng)格,被稱為“芬蘭式命名法”(Finnish Notation)
const source$ = new Observable((observer)=>{
observer.next(1);
observer.next(2);
observer.complete();
// 異常處理
observer.error('error');
}); - 操作符
通過 pipe - 管道,提供的各種操作符來實(shí)現(xiàn)各種數(shù)據(jù)流的轉(zhuǎn)化和操作。
包括:創(chuàng)建型/轉(zhuǎn)化型/過濾型/合并型/錯(cuò)誤處理型
import { ajax } from 'rxjs/ajax';
import { retry } from 'rxjs/operators';
ajax('https://example.com/api/data').pipe(
// 最多重新訂閱 3 次,如果仍然失敗,則輸出錯(cuò)誤信息
retry(3)
).subscribe(
response => console.log(response),
error => console.log('Request failed after multiple attempts:', error)
);
- 觀察者 - 訂閱可觀察對(duì)象
import { Subject } from 'rxjs';
const source$ = new Subject();
// 訂閱
const sub = source$.subscribe({
next:(data)=>{}
complete:()=>{}
error:(err)=>{}
});
// 取消訂閱
sub.unsubscribe();
3. Rxjs 中的狀態(tài)
- 未開始:Observable 尚未開始發(fā)出任何值。這是 Observable 的初始狀態(tài)。
- 進(jìn)行中:Observable 正在發(fā)出值,可以是零個(gè)或多個(gè)。在這種狀態(tài)下,觀察者可以訂閱并處理這些發(fā)出的值。
- 完成:Observable 成功地發(fā)出了所有值,并已經(jīng)終止。在這種狀態(tài)下,觀察者可以做相應(yīng)的清理工作。
- 錯(cuò)誤:Observable 在發(fā)出值的過程中遇到錯(cuò)誤,終止并發(fā)送錯(cuò)誤通知。觀察者可以處理這個(gè)錯(cuò)誤通知。
注意:rxjs 只有一種終結(jié)狀態(tài),要么完成,要么出錯(cuò)。當(dāng) Observable 處于已經(jīng)完成或錯(cuò)誤狀態(tài)時(shí),是不再允許通過 next 方法發(fā)送新的數(shù)據(jù)的。一旦 Observable 進(jìn)入完成或錯(cuò)誤狀態(tài),它就不會(huì)再發(fā)出任何新的值。如果需要在完成或錯(cuò)誤狀態(tài)后再次發(fā)送新的數(shù)據(jù),可以使用其他操作符或技術(shù)來處理,例如創(chuàng)建一個(gè)新的 Observable,或者使用錯(cuò)誤處理操作符捕獲并轉(zhuǎn)換錯(cuò)誤。 - RxJS 與傳統(tǒng) JavaScript 異步編程之間的對(duì)比:
- 響應(yīng)式編程:RxJS 提供了一種響應(yīng)式編程的范式,其中數(shù)據(jù)流被建模為可觀察對(duì)象,能夠在時(shí)間上發(fā)出值。你可以訂閱這些可觀察對(duì)象,以便在內(nèi)部值發(fā)生變化時(shí)執(zhí)行相應(yīng)的操作。 傳統(tǒng) JavaScript 異步編程: 傳統(tǒng)的 JavaScript 異步編程使用回調(diào)函數(shù),事件處理程序或 Promise 來處理異步操作。
- 異步控制:RxJS 提供了一套強(qiáng)大的操作符,用于控制異步代碼的執(zhí)行順序和并發(fā)性。你可以使用操作符如 mergeMap、switchMap 和 concatMap 來處理各種異步操作,并管理它們的執(zhí)行順序和結(jié)果。 傳統(tǒng) JavaScript 異步編程: 在傳統(tǒng)的 JavaScript 異步編程中,你可能會(huì)使用嵌套的回調(diào)函數(shù)或 Promise 鏈來控制異步操作的順序。
- 組合和轉(zhuǎn)換操作:RxJS 提供了許多操作符,用于組合和轉(zhuǎn)換可觀察對(duì)象。例如,map 操作符可以將一個(gè)可觀察對(duì)象的每個(gè)值映射到一個(gè)新值,filter 操作符可以過濾出符合特定條件的值。這些操作符使得處理數(shù)據(jù)流變得更加方便和靈活。 傳統(tǒng) JavaScript 異步編程: 在傳統(tǒng)的 JavaScript 異步編程中,操作數(shù)據(jù)流通常需要手動(dòng)編寫循環(huán)和條件語句。
- 錯(cuò)誤處理:RxJS 提供了豐富的錯(cuò)誤處理機(jī)制,例如通過 catchError 操作符捕獲錯(cuò)誤并返回備用值,或者使用 retry 操作符重新訂閱可觀察對(duì)象以重試操作。 傳統(tǒng) JavaScript 異步編程: 在傳統(tǒng)的 JavaScript 異步編程中,錯(cuò)誤處理通常使用 try-catch 塊或者 Promise 的 catch 方法進(jìn)行處理。
總的來說,RxJS 提供了一個(gè)強(qiáng)大而靈活的工具集,用于處理異步和事件驅(qū)動(dòng)的代碼。它可以幫助開發(fā)者將復(fù)雜的異步操作轉(zhuǎn)化為簡潔、可讀性強(qiáng)的代碼,并能有效地處理錯(cuò)誤、控制執(zhí)行順序和進(jìn)行數(shù)據(jù)轉(zhuǎn)換。 - Rxjs 與 Promise 對(duì)比 : 加強(qiáng)版的Promise
Promise 的寫法
// Promise 的三種狀態(tài),pending, fulfilled, rejected
// Promise 定義出來 狀態(tài)變?yōu)閜ending
const p = new Promise((resolve,reject)=>{
resolve(); // 狀態(tài)變?yōu)閒ulfilled
reject(); // 狀態(tài)變?yōu)?rejected
});
p.then(()=>{}).catch().finally();
// 對(duì)比Observable
const s$ = new Observable((observer)=>{
// 對(duì)比promise ,observable 可以發(fā)送多個(gè)值,而Promise 只能發(fā)送一個(gè)值就會(huì)進(jìn)行完結(jié)狀態(tài)
// 這兒有點(diǎn)類似 ajax 和 websocket 的味道了
observer.next();
observer.next();
observer.compolete();
observer.error();
});
s$.pipe().subscribe({
next:(v)=>{// 接受數(shù)據(jù)},
complete:()=>{ // 完成狀態(tài)后的回調(diào)},
error:(err)=>{//獲取 observer.error() 發(fā)送過來的值}
});
// 使用完成后需要取消訂閱
s$.unsubscsribe();
RxJS 和 Promise 都是處理異步編程的強(qiáng)大工具,但它們?cè)谝恍┓矫嬗兴煌O旅媸?RxJS 相對(duì)于 Promise 的一些優(yōu)點(diǎn):
- 組合性和靈活性:RxJS 提供了豐富的操作符,允許你以各種方式組合和轉(zhuǎn)換可觀察對(duì)象,使其更加靈活。你可以使用 map、filter、mergeMap 等操作符來處理數(shù)據(jù)流,而不僅僅是處理單個(gè)值。這樣可以使你的代碼更加簡潔、可讀性更強(qiáng),并且更容易進(jìn)行復(fù)雜的異步操作。
- 錯(cuò)誤處理:RxJS 提供了一套完善的錯(cuò)誤處理機(jī)制,你可以使用 catchError 操作符捕獲錯(cuò)誤并返回備用值,或使用 retry 操作符進(jìn)行錯(cuò)誤重試。這使得在處理異步操作時(shí)更加容易處理和管理錯(cuò)誤情況。
- 取消和中斷:RxJS 提供了 unsubscribe 方法,可以取消訂閱可觀察對(duì)象并中斷正在進(jìn)行的異步操作。這對(duì)于在某些條件滿足時(shí)取消異步操作非常有用,而 Promise 并沒有直接提供這樣的機(jī)制。
- 動(dòng)態(tài)數(shù)據(jù)流:RxJS 的可觀察對(duì)象是動(dòng)態(tài)的,意味著你可以在需要時(shí)動(dòng)態(tài)地添加、移除或改變流中的值。這使得操作流程非常靈活,可以根據(jù)實(shí)際需要進(jìn)行動(dòng)態(tài)調(diào)整。
需要注意的是,Promise 也有其優(yōu)勢(shì)。Promise 的語法相對(duì)簡單,容易上手,并且被廣泛支持和采用。它在處理單個(gè)值的異步操作時(shí)非常方便。如果你的需求只是處理一次性的異步操作,那么 Promise 可能已經(jīng)足夠滿足你的需求。
綜上所述,RxJS 相對(duì)于 Promise 在處理復(fù)雜的、多個(gè)值的異步操作以及靈活性方面具有一定的優(yōu)勢(shì)。它提供了更多的工具和操作符,使得處理和組合異步操作更加方便和可讀。然而,選擇使用 RxJS 還是 Promise 取決于你的具體需求和個(gè)人偏好。
二 為什么要使用rxjs ? - 優(yōu)秀的異步數(shù)據(jù)流處理 (異步具有時(shí)效性)
- 豐富的operator 來操控?cái)?shù)據(jù)流
做組件之間的通信,觀察者模式(Subject直接使用 )。畫布變化事件,主要是將拖拽縮放的快交互,從React的更新體系中剝離出來,因?yàn)榍罢叩娜蝿?wù)調(diào)度影響了整體更新的流暢度
而不能直接定義emit的key - key是動(dòng)態(tài)獲取的,這種情況,采取發(fā)布訂閱模式 - 事件總線的形式 - Canvas 模擬事件
- 大量的異步數(shù)據(jù)更新,并且這些數(shù)據(jù)之間還存在相互依賴的情況
- 通過 RxJS 整合了異步編程、時(shí)序控制以及各種遵循函數(shù)式編程思想的 Operators 的特性,優(yōu)雅的實(shí)現(xiàn)了這種復(fù)雜的狀態(tài)機(jī)
三 rxjs語法
生產(chǎn)者 Observable
觀察者 Observer ( .subscribe)
operators
ajax請(qǐng)求 ,websocket,event事件
同步數(shù)據(jù)源
Of
range(1,100) 產(chǎn)生 1-100 的數(shù)據(jù)源
From
異步數(shù)據(jù)源
Interval
Timer
fromEvent
ajax
webSoket
組合數(shù)據(jù)源
Merge
combineLatest
Observable
被觀察者
const source$ = new Observable((subscriber)=>{
// 為啥不能解構(gòu) - 因?yàn)閠his綁定
subscriber.next();
subscriber.next();
subscriber.complete();
subscriber.error();
});
觀察者
source$ .subscribe({
next:()=>{},
complete:()=>{},
error:()=>{}
});
Subject
多播- Subject 是一種特殊類型的可觀察對(duì)象(Observable),同時(shí)也是觀察者(Observer)。它允許多個(gè)觀察者訂閱它,并且可以通過調(diào)用 next 方法來向觀察者發(fā)送新的值。
數(shù)據(jù)流既可以 next, complete, error
也可以 subscibe
注意:next發(fā)送的值,只能被已經(jīng)訂閱的觀察者接收到。
注意:
多播也可以用作 takeUntil 的參數(shù),用戶手動(dòng)來
將流變成完成時(shí)
BehaviorSubject
對(duì)比Subject
BehaviorSubject 在被訂閱時(shí)會(huì)立即發(fā)送其最近的值給訂閱者 (可以拿到訂閱前最新一次的值)。而Subject不能傳遞初始值,且訂閱后才能接受next發(fā)送的值,訂閱之前的值,都拿不到。
of
在 RxJS 中,"of" 是一個(gè)創(chuàng)建 Observable 的操作符。它可以用來創(chuàng)建一個(gè)包含指定值的 Observable,并且這些值會(huì)按順序依次發(fā)出。
of(a,b,c) //會(huì)依次發(fā)送 a,b,c 三個(gè)值
of([a,b,c]) // 會(huì)發(fā)送一個(gè) 數(shù)組值
of是同步發(fā)送值,并且發(fā)送值以后狀態(tài)變成 complete
Interval (timer)
Interval (1000)每隔1000ms 連續(xù)發(fā)送數(shù)據(jù),從0開始累加
timer(1000)隔1000ms發(fā)送數(shù)據(jù)0,發(fā)送一個(gè)數(shù)據(jù)后,進(jìn)入完成狀態(tài)
from
將其他類型的數(shù)據(jù)源轉(zhuǎn)或者數(shù)據(jù)結(jié)構(gòu)化為Observable
- 數(shù)組(數(shù)組同 of 數(shù)組不同,from 數(shù)組會(huì)依次發(fā)送,of 中數(shù)組會(huì)把數(shù)組當(dāng)成一個(gè)整體進(jìn)行發(fā)送)
- 轉(zhuǎn)化Promise。內(nèi)部實(shí)現(xiàn)會(huì)手動(dòng)調(diào)用promise中的 resolve或者reject,然后將返回值進(jìn)行發(fā)。
- 轉(zhuǎn)化Iterotor 迭代器對(duì)象。 上面的數(shù)組,也就是迭代器對(duì)象,會(huì)依次調(diào)用next,將迭代器對(duì)象中每個(gè)值進(jìn)行發(fā)送
fromEvent
將event 事件轉(zhuǎn)化為 Observable
merge
并行聚合 , 按照發(fā)送時(shí)間的先后順序,將多個(gè)源值并行為一個(gè)值。
of (1,2)
of(3,4,5)
Merge 后得到值, 1,2,3,4,5 。按照時(shí)間先后順序進(jìn)行發(fā)送,而不會(huì)進(jìn)行組合
—{count} +
一般用于不同的數(shù)據(jù)源返回相同的數(shù)據(jù)類型,
。觀察者不關(guān)心是哪個(gè)數(shù)據(jù)發(fā)送的數(shù)據(jù),只關(guān)心
數(shù)據(jù)時(shí)間以及數(shù)據(jù)本身。
race
race(a1$, a2$,a3$) 哪一個(gè)流先發(fā)送數(shù)據(jù),后續(xù)就一直訂閱這個(gè)流的數(shù)據(jù)。其他流全部放棄。
combineLatest
combineLatest([s1$, s2$, s3$] ).subscribe(([s1,s2,s3])=>{
// 操作
})
當(dāng) s1$, s2$, s3$ 都返回至少一個(gè)值時(shí),會(huì)發(fā)送出一個(gè)值[s1,s2,s3],然后每次 三個(gè)流中有一個(gè)流發(fā)送變化都會(huì)發(fā)送出最新的[s1,s2,s3]
const serch$ = xxx;
const currentPage$ = xxx;
combineLatest([serch$,currentPage$ ]) .subscribe(([serch,currentPage])=>{
// 轉(zhuǎn)化成其他源文件
});
forkJoin
forkJoin( [a1$,a2$,a3$] ) , 需要等到 a1$, a2$, a3$ 流都進(jìn)入完成時(shí),最后返回一次 完成時(shí)最后一次發(fā)送的數(shù)據(jù) 數(shù)組[a1,a2,a3]
其中有一個(gè)流未進(jìn)入完成時(shí),都不會(huì)最終返回結(jié)果。
可以使用 take(1)讓流進(jìn)入完成時(shí)
有點(diǎn)類似于 Promise.all
animationFrameScheduler
任務(wù)調(diào)度:類似于 window.requestAnimationFrame 每一幀去去篩選數(shù)據(jù)
Operator (寫在pipe中)
startWith
以什么開頭,給一個(gè)默認(rèn)值
map
轉(zhuǎn)化數(shù)據(jù) ,輸入的是一個(gè)函數(shù)
mapTo (已廢除)
轉(zhuǎn)化為常量。廢除了,直接用map(()=> 常量)來表示,過多的 operator 會(huì)增加知識(shí)復(fù)雜度
scan
類似于reduce積累函數(shù) (可以累積發(fā)送的值,也可以累積是第幾次發(fā)送的值,并和當(dāng)次的值進(jìn)行組合返回)
withLatestFrom
withLatestFrom操作符是一種在源Observable發(fā)出值時(shí)將其與其他Observable的最新值進(jìn)行組合的方式。當(dāng)源Observable完成時(shí),withLatestFrom不會(huì)觸發(fā)完成回調(diào),并且不會(huì)再生成任何結(jié)果。source1$.pipe(withLagestFrom(second$)).subscribe(([s1,s2])=>{
// 保證每個(gè)source1發(fā)送的數(shù)據(jù),都對(duì)應(yīng) second$數(shù)據(jù)為最新的值
})
注意,在外層源發(fā)出數(shù)據(jù)時(shí),需要確保內(nèi)層源已經(jīng)發(fā)送出數(shù)據(jù)了(至少一個(gè)),不然取不到值。
也就是,每次外層源發(fā)送一個(gè)值,就去內(nèi)層源中取 上次最新發(fā)送的值 進(jìn)行組合。
switchMap
switchMap 接受一個(gè)函數(shù)作為參數(shù),該函數(shù)將源 Observable 的值映射為一個(gè)新的 Observable。每當(dāng)源 Observable 發(fā)出新的值時(shí),switchMap 會(huì)取消訂閱前一個(gè)內(nèi)部 Observable,并訂閱最新的內(nèi)部 Observable。這樣可以確保只有最新的內(nèi)部 Observable 的值被發(fā)出,而忽略前一個(gè)內(nèi)部 Observable 的值。
當(dāng)外層數(shù)據(jù)產(chǎn)生新值時(shí),內(nèi)層的數(shù)據(jù)源訂閱會(huì)被取消掉
從而確保內(nèi)部源使用到的外部源的數(shù)據(jù)總是最新的
可用于解決翻頁渲染問題。例如,我們翻頁,從第一頁,翻頁到第二頁
又快速翻頁到第三頁。如果出現(xiàn)網(wǎng)絡(luò)問題。
switchMap 的使用,當(dāng)翻頁數(shù)據(jù)到達(dá)第三頁時(shí),如果第二頁還沒有返回?cái)?shù)據(jù),訂閱
訂閱會(huì)被取消,就算第二頁數(shù)據(jù)返回了,也不會(huì)監(jiān)聽到。
mergeMap
串行聚合(有先后順序的聚合)
mousedow - mousemove(takeUntil - mouseup)
concatMap
用于將一個(gè)外部 Observable 序列中的每個(gè)值映射為內(nèi)部Observable,并按照順序依次訂閱內(nèi)部Observable。當(dāng)一個(gè)內(nèi)部Observable完成后,才會(huì)開始訂閱下一個(gè)內(nèi)部Observable。(用于同步數(shù)據(jù)轉(zhuǎn) 其他數(shù)據(jù)源,當(dāng)內(nèi)部的其他數(shù)據(jù)源完成時(shí)狀態(tài)時(shí),才會(huì)去訂閱 外部源的另外一個(gè)發(fā)送過來的數(shù)據(jù) - 但 外部同步數(shù)據(jù)源肯定是沒有延遲的,同時(shí)發(fā)送了所有數(shù)據(jù),但是只是被緩存了,沒有進(jìn)行訂閱而已)
tap
拿傳過的來值進(jìn)行一次不影響后續(xù) Stream 的 “純操作”,
通過 tap 操作符進(jìn)行 RxJS 的 Debug
take
取具體幾個(gè)數(shù)據(jù),然后滿足條件后數(shù)據(jù)變成完成時(shí)。
take(3), 當(dāng)流發(fā)送3個(gè)數(shù)據(jù)后,進(jìn)入完成時(shí)。
takeUntil
獲取值,直到某個(gè)流發(fā)送數(shù)據(jù)為止,并且讓當(dāng)前流進(jìn)入完成時(shí)狀態(tài)
參數(shù)為一個(gè)函數(shù),函數(shù)的返回值為一個(gè)新的流,當(dāng)這個(gè)新的流
發(fā)送第一個(gè)數(shù)據(jù)的時(shí)候。外層的流就會(huì)終止訂閱
takeWhile
中一個(gè)常用的操作符是takeWhile,它可以根據(jù)條件判斷是否繼續(xù)發(fā)送數(shù)據(jù)或完成流。
delay
讓流的發(fā)送間隔一段時(shí)間再繼續(xù)發(fā)送
delay(300) 延遲300ms
catchError
它用于捕獲 Observable 中的錯(cuò)誤并處理這些錯(cuò)誤。
相當(dāng)于拿到報(bào)錯(cuò)信息,做一層處理,然后return 的值會(huì)被,subscribe 的的第二個(gè)參數(shù) - 處理報(bào)錯(cuò)信息函數(shù)監(jiān)聽到。
retryWhen
在rxjs中,retryWhen運(yùn)算符用于重新訂閱一個(gè)Observable對(duì)象,只有當(dāng)retryWhen返回的新Observable對(duì)象發(fā)出complete通知時(shí),才會(huì)停止重新訂閱。
類似Promise, 當(dāng)進(jìn)入錯(cuò)誤狀態(tài)后,流狀態(tài)就會(huì)進(jìn)入異常狀態(tài),不會(huì)再進(jìn)行監(jiān)聽
retryWhen(errors$ => {
// 控制重試邏輯
return errors$
.pipe(
delay(1000) // 延遲1秒后重新訂閱
)
.pipe(
// 可以添加更多的操作符
take(3) // 重試最多3次
);
})
delayWhen
s1$.pipe(delayWhen(()=> s2$))
當(dāng) s2$ 流發(fā)送數(shù)據(jù)時(shí), s1$ 才會(huì)發(fā)送數(shù)據(jù),不過s1數(shù)據(jù)會(huì)不斷累積,在s2發(fā)送數(shù)據(jù)那一刻,全部爆發(fā),一次性發(fā)送過來。
distinctUntilChanged
直到源文件發(fā)送的值發(fā)生改變后,才發(fā)送數(shù)據(jù)
Const currentPage$ = currentSubject$.pipe(distinctUntilChange())
debounceTime
防抖
debounceTime(300)
throttleTime
節(jié)流
throttleTime(300)
observeOn
調(diào)度器
Import { animationFrameScheduler } from 'rxjs';
- asyncScheduler: 調(diào)度器可以在異步上下文中執(zhí)行操作符和觀察者。它通過使用 setTimeout 來異步調(diào)度任務(wù)。
- queueScheduler: 調(diào)度器會(huì)按順序執(zhí)行任務(wù),并且每個(gè)任務(wù)之間有一個(gè)最小延遲時(shí)間。它使用 setTimeout 進(jìn)行任務(wù)調(diào)度
- asapScheduler:調(diào)度器用于盡快執(zhí)行任務(wù),優(yōu)先于 setTimeout。它使用 setImmediate(在支持的環(huán)境中)或者 setTimeout(在不支持 setImmediate 的環(huán)境中)進(jìn)行調(diào)度。
- animationFrameScheduler: 是用來執(zhí)行與動(dòng)畫相關(guān)的任務(wù),基于瀏覽器的動(dòng)畫幀調(diào)度器。
- virtualTimeScheduler: 是一個(gè)虛擬的調(diào)度器,主要用于測(cè)試目的。它使用虛擬的時(shí)間概念,可以提供對(duì)時(shí)間的精確控制
使?了asap,是指產(chǎn)?全部數(shù)據(jù)的時(shí)間跨度是215毫秒,?
不是獨(dú)占唯?的線程215毫秒,asap把產(chǎn)?每?個(gè)數(shù)據(jù)的?作都通過Micro Task來實(shí)現(xiàn),這多繞了個(gè)路,時(shí)間跨度當(dāng)然?,但是這也避免了同步調(diào) ?,在這215毫秒?,其他插?的微任務(wù)可以獲得執(zhí)?的機(jī)會(huì),如果是在瀏 覽器中執(zhí)?,?頁中的渲染和對(duì)?戶操作的響應(yīng)也可以在事件處理的間隙
獲得執(zhí)?的機(jī)會(huì)。
使?asap這樣的Scheduler,?的是提?整體的感知性能,?不 是為了縮短單獨(dú)?個(gè)數(shù)據(jù)流的執(zhí)?時(shí)間。
asap會(huì)盡量使?Micro Task,?async利?的是Macro Task
queue這個(gè)Scheduler,如果調(diào)?它的schedule函數(shù)式參數(shù)delay是0,那 它就?同步的?式執(zhí)?,如果delay參數(shù)?于0,那queue的表現(xiàn)其實(shí)就和 async?模?樣
調(diào)度器
時(shí)間調(diào)度:requestAnimationFrame - raf
驗(yàn)證 mergeMap 和 switchMap 的區(qū)別
of(1000, 300, 200, 100)
.pipe(
// switchMap((v) => of(v).pipe(delay(v))),
mergeMap((v) => of(v).pipe(delay(v))),
//observeOn(animationFrameScheduler),
)
.subscribe((v) => {
console.log(v);
});
四 什么情況下需要使用rxjs
- 出現(xiàn)多重異步的情況,使用promise 或者async/await 都有點(diǎn)吃力,寫出來的代碼耦合度高、雜亂不易理解。
- 需要使用觀察者模式進(jìn)行監(jiān)聽發(fā)布時(shí),可以考慮使用rxjs
使用rxjs目的,減少異步書寫難度,減少代碼耦合、增強(qiáng)代碼可維護(hù)性。
注意,一般能使用promise、async/await 解決的簡單問題,不建議使用rxjs。
五 案例介紹 - 需求一: 用rxjs 實(shí)現(xiàn)一個(gè)簡單的 excel 表格
- 需求二:實(shí)現(xiàn)一個(gè)表單搜索與表格翻頁于一體的功能
- input框輸入要進(jìn)行 防抖操作
- 翻頁數(shù)字必須變化才會(huì)進(jìn)行搜索
- 最后渲染的數(shù)據(jù)必須同搜索的數(shù)據(jù)流一致(防止因某些網(wǎng)絡(luò)原因或者服務(wù)器原因,導(dǎo)致前進(jìn)行請(qǐng)求的數(shù)據(jù)后返回的情況)
- 需求三: 你畫我猜 游戲
- 鼠標(biāo)按下,鼠標(biāo)移動(dòng)根據(jù)鼠標(biāo)移動(dòng)的軌跡進(jìn)行線條繪制
- 鼠標(biāo)彈起,停止繪制
- 需求四:鍵盤按鍵觸發(fā)彩蛋- 大招
- 比如,需要在3秒內(nèi)觸發(fā) 上下下左上右下 這幾個(gè)按鍵就能觸發(fā)一個(gè)彩蛋。
- 需求五:使用rxjs 實(shí)現(xiàn) canvas 事件系統(tǒng)
參考文檔: - Rxjs 官網(wǎng)(英文)
- Rxjs 官網(wǎng) (中文翻譯版)
- 學(xué)習(xí)rxjs 操作符 中文版
- 《深入淺出Rxjs》
- 從業(yè)務(wù)邏輯聊聊為什么我們需要Rxjs?
- 異步復(fù)雜到什么程度才需要使用rxjs?
- 彈珠圖
該文章在 2024/11/12 11:24:54 編輯過