從命令式到響應式(九)

使用rxjs時何時取消訂閱是咱們必需要關心的,這個系列的前面幾篇也提到過,原則是儘可能不去手動訂閱流,但手動訂閱終究是沒法避免的,今天主要總結下如何適時的取消訂閱。web

讓angular幫咱們取消

這個不用多說了,主要是採用 async pipe,在HTML模版本中讓angular自已去取數據,再自動的取消。數組

在component中管理subscription

思路是,在component中訂閱的流,在合適的生命週期中取消,最多見就是在OnDestroy的時候。兩種方式,第一種組件維護一個subscription,其它的subscription經過add方法添加至這個subscription上,取消的時候調用這個subscription的unsubscribe方法,第二種,組件維護一個subscription數據,取消的時候遍歷數組,調用每個subscription的unsubscribe方法。websocket

假設咱們有一個服務,能夠發送websocket的請求,處理請求的錯誤,同時還能夠提供一些公共邏輯的處理,大概像下面這樣:socket

// service.ts
@Injectable()
export class MyService {
    constructor(public websocket: WebsocketService) {}

    // 發送websocket請求
    request(paramObs: Observable<MyInterface>): Subscription {
        return paramObs.subscribe(params => this.websocket.send(params));
    }

    // 處理響應的錯誤
    handleError(): Subscribe {
        return this.websocket.message.pipe(
            filter(res => res.flag === 'request flag') // 取這個上面請求的結果
        ).subscribe(res => ...)
    }

    // 其它須要在服務中訂閱後處理的邏輯
    otherLogic(params: Observable<any>): Subscription {
        return params.subscribe(...) 
    }
}

第一種思路:async

@Component({...})
export class MyComponent implement OnInit, OnDestroy {
    subscription: Subscription;

    constructor(private ser: MyService) { }

    ngOnInit() {
        this.subscription = this.ser.request(paramsObs1) // 參數是包含請求數據的流
            .add(this.otherLogic(paramsObs2)) // 參數是須要處理的數據流
            .add(this.ser.handleError())
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }
}

第二種思路:函數

@Component({...})
export class MyComponent implement OnInit, OnDestroy {
    subscriptions: Subscription[] = [];

    constructor(private ser: MyService) { }

    ngOnInit() {
        this.subscriptions = [
            this.ser.request(paramObs1), // 參數是包含請求數據的流
            this.ser.handleError(),
            this.otherLogic(paramsObs2) // 參數是須要處理的數據流
        ];
    }

    ngOnDestroy() {
        this.subscriptions.forEach(sub => sub.unsubscribe());
    }
}

除了寫法上的不一樣外,最大的不一樣在於採用第一種寫法時,你可能須要注意添加的順序,假如例子中的paramsObs2參數流會出完成通知則會致使handleError也被取消掉,這種場景你們能夠本身寫個demo試下,第二種寫法則不會,但可能重複去取消一些已經被取消過的流,好在這並不會致使錯誤的發生。this

使用rxjs的操做符管理訂閱

思路是使用那些可讓流發出結束通知的操做符,將其添加到須要管理的流上,讓rxjs自動取消訂閱。經常使用的有下面這些:spa

take操做符

此操做符會上當前流上取指定數量的值,而後發出完成通知,使用只想讓otherLogic處理前3個數據,code

ngOnInit() {
    this.ser.otherLogic(paramsObs2.take(3)); // 再也不須要理會訂閱產生的subscription了,處理3個值後自動取消訂閱;
}

相似的操做符還有first,from,of等都會發出完成通知。component

takeWhile操做符

此操做符添加到流上後,每一次發出值時都要檢查操做符中傳入的斷定函數返回的結果是否爲true,一旦返回false,輸出流將會被取消掉,再也不發出值,假設咱們的paramsObs2的數據來自於一個formControl:

@Component({
    ...
    template: `<input type="text" [formControl]="control">`
})
export class MyComponent implement OnInit, OnDestroy {
    control = new FormControl('xxx');

    isAlive = true; // 添加一個變量控制流的結束

    ...

    ngOnInit() {
        ...
        this.ser.otherLogic(this.control.valueChanges.pipe(
            takeWhile(() => this.isAlive) // 傳入了一個斷定函數
        ))
    }

    ngOnDestroy() {
        this.isAlive = false; // 這裏變爲false;
    }
}

takeUntil操做符

此操做符和takeWhile不一樣,第一,接受的參數不是斷定函數,而是Observable, 第二,傳入的observable一旦發出值,輸入流將會被取消訂閱,無論發出的值是true仍是fasle,或者是其它值。

@Component({
    ...
    template: `<input type="text" [formControl]="control">`
})
export class MyComponent implement OnInit, OnDestroy {
    control = new FormControl('xxx');

    constructor(
        ...
        private router: Router
    ){}

    ngOnInit() {
        ...
        this.ser.otherLogic(this.control.valueChanges.pipe(
            takeWhile(this.router.events) // 把router的事件流傳了進去,只要router上有事件發出輸出流就被取消
        ))
    }

    ...
}

這幾種方法各有各有的特色,有的須要額外的變量但用起來簡單粗爆,有的簡潔明瞭但你須要花心思在其它條件上,在項目中能夠根據實際狀況選擇最適合的使用。

圖片描述

相關文章
相關標籤/搜索