RxJS是一種針對異步數(shù)據(jù)流編程工具,或者叫響應(yīng)式擴展編程;可不管如何解釋RxJS其目標就是異步編程,Angular引入RxJS為了就是讓異步可控强霎、更簡單。
而今就是要探討什么是Observable蓉冈、observer城舞、operator、Submit寞酿、EventEmmit家夺,以及如何去使用它們。
什么是Observable伐弹?
Observable只是一個普通函數(shù)拉馋,要想讓他有所作為,就需要跟observer一起使用惨好;前者是受后者是攻煌茴。而這個observer(后面我們會介紹)只是一個帶有 next
、error
日川、complete
的簡單對象而已蔓腐。最后,還需要通過 subscribe
訂閱來啟動Observable逗鸣;否則它是不會有任何反應(yīng);可以理解為陌為了他們能在一起而提供的環(huán)境*绰精,而訂閱也會返回一個可用于取消操作(在RxJS里叫 unsubscribe
)撒璧。
當Observable設(shè)置觀察者后,而連接并獲取原始數(shù)據(jù)的這個過程叫生產(chǎn)者笨使,可能是DOM中的 click
事件卿樱、input
事件、或者更加復(fù)雜的HTTP通信硫椰。
為了更好理解繁调,先從一個簡單的示例開始:
import { Component } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
@Component({
selector: 'app-home',
template: `<input type="text"> `
})
export class HomeComponent {
ngOnInit() {
const node = document.querySelector('input[type=text]');
// 第二個參數(shù) input 是事件名萨蚕,對于input元素有一個 oninput 事件用于接受用戶輸入
const input$ = Observable.fromEvent(node, 'input');
input$.subscribe({
next: (event: any) => console.log(`You just typed ${event.target.value}!`),
error: (err) => console.log(`Oops... ${err}`),
complete: () => console.log(`Complete!`)
});
}
}
示例中 Observable.fromEvent()
會返回一個Observable,并且監(jiān)聽 input
事件蹄胰,當事件被觸發(fā)后會發(fā)送一個 Event
給對應(yīng)的observer觀察者岳遥。
什么是observer?
observer非常簡單裕寨,像上面示例中 subscribe
訂閱就是接收一個 observer 方法浩蓉。
一般在Angular我們 subscribe
會這么寫:
input$.subscribe((event: any) => {
});
從語法角度來講和 subscribe({ next, error, complete })
是一樣的。
當Observable產(chǎn)生一個新值時宾袜,會通知 observer 的 next()
捻艳,而當捕獲失敗可以調(diào)用 error()
。
當Observable被訂閱后庆猫,除非調(diào)用observer的 complete()
或 unsubscribe()
取消訂閱兩情況以外认轨;會一直將值傳遞給 observer。
Observable的生產(chǎn)的值允許經(jīng)過一序列格式化或操作月培,最終得到一個有價值的數(shù)據(jù)給觀察者嘁字,而這一切是由一序列鏈式operator來完成的,每一個operator都會產(chǎn)生一個新的Observable节视。而我們也稱這一序列過程為:流拳锚。
什么是operator?
正如前面說到的寻行,Observable可以鏈式寫法霍掺,這意味著我們可以這樣:
Observable.fromEvent(node, 'input')
.map((event: any) => event.target.value)
.filter(value => value.length >= 2)
.subscribe(value => { console.log(value); });
下面是整個順序步驟:
- 假設(shè)用戶輸入:a
- Observable對觸發(fā)
oninput
事件作出反應(yīng),將值以參數(shù)的形式傳遞給observer的next()
拌蜘。 -
map()
根據(jù)event.target.value
的內(nèi)容返回一個新的 Observable杆烁,并調(diào)用next()
傳遞給下一個observer。 -
filter()
如果值長度>=2
的話简卧,則返回一個新的 Observable兔魂,并調(diào)用next()
傳遞給下一個observer。 - 最后举娩,將結(jié)果傳遞給
subscribe
訂閱塊析校。
你只要記住每一次 operator 都會返回一個新的 Observable,不管 operator 有多少個铜涉,最終只有最后一個 Observable 會被訂閱智玻。
不要忘記取消訂閱
為什么需要取消訂閱
Observable 當有數(shù)據(jù)產(chǎn)生時才會推送給訂閱者,所以它可能會無限次向訂閱者推送數(shù)據(jù)芙代。正因為如此吊奢,在Angular里面創(chuàng)建組件的時候務(wù)必要取消訂閱操作,以避免內(nèi)存泄漏纹烹,要知道在SPA世界里懂得擦屁股是一件必須的事页滚。
unsubscribe
前面示例講過召边,調(diào)用 subscribe()
后,會返回一個 Subscription
可用于取消操作 unsubscribe()
裹驰。最合理的方式在 ngOnDestroy
調(diào)用它隧熙。
ngOnDestroy() {
this.inputSubscription.unsubscribe();
}
takeWhile
如果組件有很多訂閱者的話,則需要將這些訂閱者存儲在數(shù)組中邦马,并組件被銷毀時再逐個取消訂閱贱鼻。但,我們有更好的辦法:
使用 takeWhile()
operator滋将,它會在你傳遞一個布爾值是調(diào)用 next()
還是 complete()
邻悬。
private alive: boolean = true;
ngOnInit() {
const node = document.querySelector('input[type=text]');
this.s = Observable.fromEvent(node, 'input')
.takeWhile(() => this.alive)
.map((event: any) => event.target.value)
.filter(value => value.length >= 2)
.subscribe(value => { console.log(value) });
}
ngOnDestroy() {
this.alive = false;
}
簡單有效,而且優(yōu)雅随闽。
Subject
如果說 Observable
與 observer
是攻受結(jié)合體的話父丰,那么 Subject
就是一個人即攻亦受。正因為如此掘宪,我們在寫一個Service用于數(shù)據(jù)傳遞時蛾扇,總是使用 new Subject
。
@Injectable()
export class MessageService {
private subject = new Subject<any>();
send(message: any) {
this.subject.next(message);
}
get(): Observable<any> {
return this.subject.asObservable();
}
}
當F組件需要向M組件傳遞數(shù)據(jù)時魏滚,我們可以在F組件中使用 send()
镀首。
constructor(public srv: MessageService) { }
ngOnInit() {
this.srv.send('w s k f m?')
}
而M組件只需要訂閱內(nèi)容就行:
constructor(private srv: MessageService) {}
message: any;
ngOnInit() {
this.srv.get().subscribe((result) => {
this.message = result;
})
}
EventEmitter
其實EventEmitter跟RxJS沒有直接關(guān)系,因為他是Angular的產(chǎn)物鼠次,而非RxJS的東西更哄。或者我們壓根沒必要去談腥寇,因為EventEmitter就是Subject成翩。
EventEmitter的作用是使指令或組件能自定義事件。
@Output() changed = new EventEmitter<string>();
click() {
this.changed.emit('hi~');
}
@Component({
template: `<comp (changed)="subscribe($event)"></comp>`
})
export class HomeComponent {
subscribe(message: string) {
// 接收:hi~
}
}
上面示例其實和上一個示例中 MessageService
如出一轍赦役,只不過是將 next()
換成 emit()
僅此而已麻敌。
結(jié)論
RxJS最難我想就是各種operator的應(yīng)用了,這需要一些經(jīng)驗的積累掂摔。
RxJS很火很大原因我認還是提供了豐富的API术羔,以下是摘抄:
創(chuàng)建數(shù)據(jù)流:
- 單值:of, empty, never
- 多值:from
- 定時:interval, timer
- 從事件創(chuàng)建:fromEvent
- 從Promise創(chuàng)建:fromPromise
- 自定義創(chuàng)建:create
轉(zhuǎn)換操作:
- 改變數(shù)據(jù)形態(tài):map, mapTo, pluck
- 過濾一些值:filter, skip, first, last, take
- 時間軸上的操作:delay, timeout, throttle, debounce, audit, bufferTime
- 累加:reduce, scan
- 異常處理:throw, catch, retry, finally
- 條件執(zhí)行:takeUntil, delayWhen, retryWhen, subscribeOn, ObserveOn
- 轉(zhuǎn)接:switch
組合數(shù)據(jù)流:
- concat,保持原來的序列順序連接兩個數(shù)據(jù)流
- merge乙漓,合并序列
- race级历,預(yù)設(shè)條件為其中一個數(shù)據(jù)流完成
- forkJoin,預(yù)設(shè)條件為所有數(shù)據(jù)流都完成
- zip簇秒,取各來源數(shù)據(jù)流最后一個值合并為對象
- combineLatest鱼喉,取各來源數(shù)據(jù)流最后一個值合并為數(shù)組
另秀鞭,最好使用 $ 結(jié)尾的命名方式來表示Observable趋观,例:input$扛禽。
happy coding!