【问题标题】:Making sure that subscription is closed确保订阅已关闭
【发布时间】:2021-09-01 06:10:12
【问题描述】:

我有一个服务,它从 2 个来源获取数据,然后对其执行一些逻辑,最重要的是,我有一个使用此服务的组件,并且应该在此逻辑完成时收到通知。我实际上有这方面的工作示例,但是我不完全确定我的订阅是否已关闭。

首先,服务代码:

@Injectable()
export class AppService {
  private myData: string[] = [];

  load(): Observable<any> {
    return combineLatest([this.loadInternal1(), this.loadInternal2()]).pipe(
      take(1),
      tap(next => {
        this.myData.push(next[0].map(el => el.toUpperCase()));
        this.myData.push(next[1].map(el => el.toLowerCase()));
      })
    );
  }

  private loadInternal1(): Observable<string[]> {
    return of(['data', 'data2']);
  }

  private loadInternal2(): Observable<string[]> {
    return of(['data3', 'data4']);
  }
}

然后是组件

export class HelloComponent {
  loading: boolean = true;

  constructor(private appService: AppService) {
    const sub$ = this.appService.load().subscribe(next => {
      console.log(next)
      this.loading = false;
    });

    setTimeout(() => {
      console.log(sub$.closed);
    }, 1000);
  }
}

据我所知,take(1) 应该在第一次发出值后自动关闭订阅,所以我不必手动关闭它。

我想检查一下,所以 1 秒后我确定是否是 sub$.closed === true,是的 - 是的。

但是,当我从我的服务实现中删除 take(1) 行时,它也被关闭了......所以我很困惑 - 我需要这个吗?或者我不明白订阅是如何工作的……?

#编辑:

此订阅不是在构造函数上发生,而是在点击时发生,如下所示:

@Component({
  selector: 'hello',
  template: `
    <button (click)="getData()">Get data</button>
  `
})
export class HelloComponent implements OnDestroy {
  loading: boolean = true;
  sub$: Subscription;

  constructor(private appService: AppService) {}

  ngOnDestroy(): void {
    this.sub$.unsubscribe();
  }

  getData(): void {
    this.sub$ = this.appService.load().subscribe(() => {
      this.loading = false;
    });
  }
}

问题是 - private sub$ 在每次点击时发生了什么?它被重新分配,对吗?所以这意味着它也会自动完成?

如果用户点击 10 次,那么他将创建 10 个订阅,其中 9 个将被关闭。那么,以关闭页面为例,ngOnDestroy 将被触发并关闭第 10 个页面?

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:

    如果你不确定,你只能这样做

    导出类组件实现 OnDestroy{

    无论你在这里写什么,你都可以摧毁所有这些东西。你有疑问。

    ngOnDestroy(){ 数据全部销毁。当组件关闭时。 } }

    【讨论】:

      【解决方案2】:

      首先不要订阅构造函数。这不是一个好的做法,有时会导致奇怪的行为(例如,有时在构造函数启动时某些数据还没有准备好)。改用 ngoninit。

      那么您必须取消订阅。我用这种方式:

      export class HelloComponent {
        loading: boolean = true;
        
        subscriptions = new Subscription; //this is like a "container" of my subscription
        constructor(private appService: AppService) { 
        }
      
        ngOnInit() {
        const sub$ = this.appService.load().subscribe(next => {
            console.log(next)
            this.loading = false;
          });
         
         this.subscriptions.add(sub$); //you have to add every subscription to our subscriptions
         
        }
      
        ngOnDestroy() {
        this.subscriptions.unsubscribe() //when the component is destroyed the subscriptions is "closed"
        }
      

      但是,如果您想在某个时刻取消订阅,您可以这样做

      export class HelloComponent {
        loading: boolean = true;
        
        private destroyed$: ReplaySubject<boolean> = new ReplaySubject();
        constructor(private appService: AppService) { 
        }
      
        ngOnInit() {
        const sub$ = this.appService.load().pipe(
         takeUntil(destroy$) //when you cast destroy$ the observable unsubscribe the subscription
        )
        .subscribe(next => {
            console.log(next)
            this.loading = false;
          });
         
        setTimeout(() => {
            this.destroy$.next()
          }, 1000);
         
        }
      

      回答您的编辑:

      我不是那么专家,所以我不确定我说的是不是 rigtt,但是当您单击时,您不会创建新订阅。订阅只有一个,它仍然在监听 load observable 发射数据。如果用户点击十次,20100次,订阅只有一次,直到你不触发流他什么都不做。

      如果我了解您的目的,我会向您展示一项服务,在某些情况下可以帮助我重新获取数据

      我的服务.ts

      export class DossierService {
        ....
      
        fetchData$$ = new BehaviorSubject<void>(null);
      
        constructor() {}
      
        onReload() {
          this.fetchData$$.next();
        }
      
        isReload(): Observable<void> {
          return this.fetchData$$.asObservable();
        }
      

      在组件中我会做

      ...
      
      ngOnInit() {
        const dossier$ = 
         this.dossierService.isReload().subscribe(...mylogic);
      this.subscriptions.add(dossier$)
      }
      
      refetchData() {
       this.service.onReload()
      }
      
      ngOnDestroy() {
       this.subscriptions.unsubscribe()
      }
      

      订阅只有一个,每次调用 refetchData() 时都会触发,在服务中推送主题

      【讨论】:

      • 这只是构造函数的示例,在实际代码中它实际上发生在点击时。在这种情况下,每次点击都会创建新订阅并重新分配private subscription,对吗?在那种情况下,每次重新分配都会关闭此订阅?最后一个将在ngOnDestroy"中销毁?
      • 我需要一些代码才能很好地理解。但是每次触发流它都会改变,但订阅只有一个,是改变的数据流。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-30
      • 2012-05-16
      • 2017-02-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多