命令式から応答式へ(九)


rxjsを使用するときにいつ購読をキャンセルするかは私たちが関心を持たなければならない.このシリーズの前のいくつかの編でも述べたように、原則はできるだけ手動で購読しないことだが、手動で購読するのは結局避けられない.今日は主にどのように適時に購読をキャンセルするかをまとめた.

angularにキャンセルしてもらいます


これは言うまでもなく、主にasync pipeを採用し、HTMLモデルバージョンでangularに自分でデータを取りに行かせ、自動的にキャンセルします.

コンポーネントでのsubscriptionの管理


アイデアは、componentで購読されたストリームが、適切なライフサイクルでキャンセルされ、最も一般的なのはOnDestroyの場合です.2つの方法では、第1のコンポーネントは1つのsubscriptionを維持し、他のsubscriptionはadd方法でこのsubscriptionに追加され、キャンセル時にこのsubscriptionのunsubscribe方法を呼び出し、第2のコンポーネントは1つのsubscriptionデータを維持し、キャンセル時に配列を遍歴し、各subscriptionのunsubscribe方法を呼び出す.
Websocketのリクエストを送信したり、リクエストのエラーを処理したり、共通の論理的な処理を提供したりするサービスがあるとします.
// service.ts
@Injectable()
export class MyService {
    constructor(public websocket: WebsocketService) {}

    //   websocket  
    request(paramObs: Observable): 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): Subscription {
        return params.subscribe(...) 
    }
}

第一の考え方:
@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();
    }
}

2つ目の考え方:
@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());
    }
}

書き方の違いを除いて、最大の違いは、1つ目の書き方を採用する場合、追加の順序に注意する必要があるかもしれません.例のparamsObs 2パラメータの流れが完了通知を出すとhandleErrorもキャンセルされます.このシーンでは、自分でdemoを書いてみてください.2つ目の書き方はできませんが、キャンセルされたストリームを繰り返しキャンセルすることができます.これは間違いを招くことはありません.

rxjsのオペレータを使用してサブスクリプションを管理する


フローに終了通知を発行できるオペレータを使用して、管理が必要なフローに追加し、rxjsが自動的にサブスクリプションをキャンセルすることを考えています.よく使われるのは次のとおりです.

takeオペレータ


このオペレータは、現在のストリームに指定された数の値を取り、otherLogicに最初の3つのデータを処理させたいだけで完了通知を発行します.
ngOnInit() {
    this.ser.otherLogic(paramsObs2.take(3)); //            subscription ,  3         ;
}

似たようなオペレータにはfirst,from,ofなどが完了通知を発行します.

takeWhileオペレータ


このオペレータがストリームに追加されると、値が発行されるたびに、オペレータに入力された判定関数が返す結果がtrueであるかどうかを確認します.falseに戻ると、出力ストリームはキャンセルされ、値は発行されません.paramsObs 2のデータがformControlから来ているとします.
@Component({
    ...
    template: ``
})
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とは異なり、第1に、受信したパラメータは判定関数ではなくObservable、第2に、入力されたobservableが値を発行すると、入力ストリームはtrueまたはfasle、または他の値にかかわらず購読をキャンセルされます.
@Component({
    ...
    template: ``
})
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             
        ))
    }

    ...
}

このいくつかの方法にはそれぞれ特徴があり、追加の変数が必要だが簡単に太く、簡潔明瞭であるが、他の条件に工夫する必要があり、プロジェクトでは実際の状況に応じて最適な使用を選択することができる.