简体   繁体   English

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

[英]Do we have to unsubscribe when using switchMap operator in rxjs in Angular 2?

In Angular 2 there are some observables that you do not need to unsubscribe from.在 Angular 2 中有一些您不需要取消订阅的可观察对象。 For example http requests and activatedRoute.params.例如 http 请求和 activatedRoute.params。

Angular/RxJs When should I unsubscribe from `Subscription` Angular/RxJs 我什么时候应该取消订阅 `Subscription`

But what happens when I use switchMap on for example activatedRoute.params and inside that switchMap I access a service that returns an observable that would need to be unsubscribed if subscribed to in the usual way.但是当我在 activatedRoute.params 上使用 switchMap 时会发生什么,在 switchMap 中我访问了一个服务,该服务返回一个 observable,如果以通常的方式订阅,则需要取消订阅。

Something like this:是这样的:

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

If I called this.userService without the switchMap and without activatedRoute.params I would have to unsubscribe from it.如果我在没有 switchMap 且没有 activatedRoute.params 的情况下调用 this.userService,我将不得不取消订阅它。

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

And then later..然后后来..

this.subscription.unsubscribe();

My question is, do I need to unsubscribe from activatedRoute.params if I use switchMap on it and call a service that would need to be unsubscribed otherwise?我的问题是,如果我在 activatedRoute.params 上使用 switchMap 并调用需要取消订阅的服务,我是否需要取消订阅?

If the source observable to which you are subscribing always completes or errors, you don't need to unsubscribe.如果您订阅的源 observable 总是完成或出错,您不需要取消订阅。

However, if you compose another observable from the source using switchMap , whether or not you need to unsubscribe depends upon the observable returned within switchMap .但是,如果您使用switchMap从源组合另一个 observable,则是否需要取消订阅取决于switchMap返回的switchMap If the returned observable does not always complete or error, then, yes, you will need to unsubscribe.如果返回的 observable 并不总是完整或错误,那么,是的,您将需要取消订阅。

If the source errors, an automatic unsubscription will occur:如果源错误,将发生自动退订:

 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>

However, if the source completes, an automatic unsubscription will not occur:但是,如果源完成,则不会发生自动取消订阅:

 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>

switchMap creates a link between the previous and new observable. switchMap在前一个和新的 observable 之间创建一个链接。 If you change the first observable, the second will be always be triggered.如果您更改第一个 observable,第二个将始终被触发。

Anything subscribed after the switchMap will be hearing changes on both, the initial observable and the returned observable.switchMap之后订阅的任何内容都将听到初始 observable 和返回的 observable 的变化。

To fully stop the first observable to update the second one or the rest is by using take , takeUntil , or takeWhile .要完全停止第一个 observable 以更新第二个或其余的可观察对象,请使用taketakeUntiltakeWhile Like:像:

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)

New RxJS documentation explains when switchMap will continue listening and when will stop.新的 RxJS 文档解释了 switchMap 何时将继续侦听以及何时停止。

See iWillContinueListening and iWillStopListening demo:参见iWillContinueListeningiWillStopListening演示:

https://www.learnrxjs.io/learn-rxjs/operators/error_handling/catch https://www.learnrxjs.io/learn-rxjs/operators/error_handling/catch

If the returned observable does not complete then an unsubscribe may be necessary.如果返回的可观察对象未完成,则可能需要取消订阅。

This answer suggests this method should be used https://stackoverflow.com/a/41177163/6088194这个答案表明应该使用这种方法https://stackoverflow.com/a/41177163/6088194

The solution we should all use going forward is to add a private ngUnsubscribe = new Subject();我们以后都应该使用的解决方案是添加一个 private ngUnsubscribe = new Subject(); field to all components that have.subscribe() calls to Observables within their class code.字段到在其 class 代码中具有对 Observables 的 subscribe() 调用的所有组件。

We then call this.ngUnsubscribe.next();然后我们调用 this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); this.ngUnsubscribe.complete(); in our ngOnDestroy() methods.在我们的 ngOnDestroy() 方法中。

The secret sauce (as noted already by @metamaker) is to call takeUntil(this.ngUnsubscribe) before each of our.subscribe() calls which will guarantee all subscriptions will be cleaned up when the component is destroyed.秘诀(正如@metamaker 已经指出的那样)是在每次调用 our.subscribe() 之前调用 takeUntil(this.ngUnsubscribe) ,这将保证在销毁组件时清除所有订阅。

However, according to this article it has potential for memory leaks when used with switchMap if the takeUntil operator is not called last in the sequence.但是,根据这篇文章,如果 takeUntil 运算符未在序列中最后调用,则在与 switchMap 一起使用时可能会发生 memory 泄漏。 So if you use this solution with switchMap then make sure takeUntil is called last in the sequence.因此,如果您将此解决方案与 switchMap 一起使用,请确保在序列中最后调用 takeUntil。

private ngUnsubscribe = new Subject();

ngOnDestroy() {
    // unsubscribe to any subscriptions that use takeUntil(ngUnsubscribe)
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

someFunction() {
    this.activatedRoute.params.pipe(
        switchMap((params: Params) => this.userService.getUser(+params['id'])),
        takeUntil(this.ngUnsubscribe)
    )
     .subscribe((user: User) => this.user = user);
}

I suggest reading both the linked answer and the linked article as it is very helpful information.我建议阅读链接的答案和链接的文章,因为它们是非常有用的信息。

Once you do a switchmap, the Subscription is attached to the last Observable.一旦你做了一个 switchmap,订阅就会附加到最后一个 Observable 上。 It doesn't matters if the first observable keeps triggering.如果第一个 observable 持续触发并不重要。 The switchmap block is only executed once. switchmap 块只执行一次。

You have to unsusbscribe from the last if this never closes.如果这永远不会关闭,您必须从最后一个取消订阅。

Check this code:检查此代码:

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();
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM