在Angular 2中的rxjs中使用switchMap运算符时,我们是否必须取消订阅?

时间:2017-06-25 17:35:19

标签: angular rxjs

在Angular 2中,有一些您不需要取消订阅的可观察对象。例如http requests和activatedRoute.params。

Angular/RxJs When should I unsubscribe from `Subscription`

但是当我使用switchMap时会发生什么情况,例如activatedRoute.params,在switchMap内部,我访问一个返回一个observable的服务,如果以通常的方式订阅则需要取消订阅。

这样的事情:

this.activatedRoute.params
    .switchMap((params: Params) => this.userService.getUser(+params['id']))
    .subscribe((user: User) => this.user = user);

如果我在没有switchMap且没有activateRoute.params的情况下调用this.userService,我将不得不取消订阅。

// userService.getUser() takes in optional id?: number.
this.subscription = this.userService.getUser().subscribe(
    (user: User) => {
        this.user = user;
    }
 );

然后...... ..

this.subscription.unsubscribe();

我的问题是,如果我在其上使用switchMap并调用需要取消订阅的服务,是否需要取消订阅activatedRoute.params?

3 个答案:

答案 0 :(得分:9)

如果您订阅的源可观察对象总是完成或错误,则您无需取消订阅。

但是,如果您使用switchMap从源中编写另一个可观察对象,则是否需要取消订阅取决于switchMap内返回的可观察对象。如果返回的observable并不总是完成或错误,那么,是的,您需要取消订阅。

如果源错误,将自动取消订阅:

const source = new Rx.Subject();
const composed = source.switchMap(() => Rx.Observable.interval(200));

composed.subscribe(value => console.log(value));
source.next(1);

setTimeout(() => {
  console.log("Erroring...");
  source.error(new Error("Boom!"));
}, 1000);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@5.4.1/bundles/Rx.min.js"></script>

但是,如果源完成,则不会发生自动取消订阅:

const source = new Rx.Subject();
const composed = source.switchMap(() => Rx.Observable.interval(200));

composed.subscribe(value => console.log(value));
source.next(1);

setTimeout(() => {
  console.log("Completing...");
  source.complete();
}, 1000);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@5.4.1/bundles/Rx.min.js"></script>

答案 1 :(得分:0)

一旦您进行了切换映射,订阅将附加到最后一个可观察对象。如果第一个可观察对象持续触发,则无关紧要。 switchmap块仅执行一次。

如果永不关闭,则必须从最后一个取消注册。

检查此代码:

import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject, Subscription} from 'rxjs';
import * as Rx from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'app works!';
  private source: Rx.Subject<any>;
  private source2: Rx.Subject<any>;
  private composed: Rx.Observable<any>;
  private composedSub: Subscription;

  public ngOnInit(): void {
    this.source = new Rx.Subject();
    this.source2 = new Rx.Subject();

    this.composed = this.source.switchMap(value => this.source2);

    this.composedSub = this.composed.subscribe(value => console.log(value));
    console.log(this.composedSub);
  }

  private onClick() {
    // Triggers the first observable, the console.log is never executed.
    this.source.next(1);
  }

  private onClick2() {
    // Console.log is executed, prints "1"
    this.source2.next(1);
  }

  private onClick3() {
    // The console log is never called again after click on this button.
    this.composedSub.unsubscribe();
  }

  private onClick4() {
    // The first observable finish. the console log keeps printing unles onClick3 is executed
    this.source.complete();
  }

  private onClick5() {
    // console.log never executed egain.
    this.source2.complete();
  }
}

答案 2 :(得分:0)

switchMap在上一个和新的可观察对象之间创建链接。如果更改第一个可观察值,则将始终触发第二个。

switchMap之后订阅的所有内容都会听到初始可观察值和返回可观察值的变化。

使用taketakeUntiltakeWhile来完全停止第一个可观察对象以更新第二个或其他对象。喜欢:

const howTimerWorks = interval(5000).pipe(
  take(2), // only get 2 responses after 5 seconds each
  switchMap(initialNumber => interval(1000)));

// 0 after 5s, then 1, 2 , 3, (new Subs) 0, 1, ... every sec, forever now.
howTimerWorks.subscribe(console.log)