是否有必要在后端服务中取消订阅/完成以防止副作用? [英] Is it necessary to unsubscribe/complete in a backend service to prevent side effects?

查看:41
本文介绍了是否有必要在后端服务中取消订阅/完成以防止副作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于何时以及如何取消订阅以及订阅不应泄漏到服务之外的问题已经有很多讨论.我已经阅读了很多关于这个问题的文章,我想我理解了一些部分,但我在文章和教程中遗漏的是如何处理我们创建的新订阅模式".

There has been a lot discussion about when and how to unsubscribe and that subscriptions shouldn't leak outside of a service. I have read many articles about this matter and I think I understood some parts, but what I'm missing in the writeups and tutorials if found, is what to do with the "new subscription pattern" we created.

所以我会先说明我说的是哪个概念,然后再提出问题.

So I will first explain from which concept I'm talking and put the question afterwards.

概念:

//some.component.ts(极度简化)

this.someSub = this.backendService.someStuff.subscribe(...);

ngOnDestroy() {
  this.someSub.unsubscribe(); // I hope we agree that it is best practice to unsubscribe here, but anyway this is not the question
}

//backend.service.ts

export class BackendService {
  private _someStuff = new BehaviorSubject<any>(null);
  readonly someStuff = this._someStuff.asObservable();

  constructor(http: HttpClient) { }

  getStuff() {
    this.http.get('...').subscribe( response => this._someStuff.next(response) );

  }

<小时>

问题:

以上是我从我的调查中找到的一个很好的解决方案,不会将 HTTP 订阅泄漏到服务之外,但也有很多关于您应该取消订阅 HTTP observables 的事实的讨论,即使它们自己完成.所以我认为这对于服务也是如此.但是我从来没有看到服务中的 HTTP 请求被取消订阅,主题也没有完成.由于我认为这是必要的,下面显示了 backenService 我希望它应该如何:

The above is what I found a good solution from my investigations to not leak the HTTP subscriptions out of the service, but there was also much discussion about the fact that you should unsubscribe form HTTP observables, even though they complete themselves. So I assume this is true for services as well. But I never saw that the HTTP requests in services were unsubscribed nor were the subjects completed. Since I expect this is necessary, the following shows the backenService how I would expect it should be:

//backend.service.ts

export class BackendService implements OnDestroy {                      // A. //
  httpSubs = new Subscription();                                        // B1. //

  private _someStuff = new BehaviorSubject<any>(null);
  readonly someStuff = this._someStuff.asObservable();

  constructor(http: HttpClient) { }

  getStuff() {
    this.httpSubs.add(this.http.get('...')                              // B2. //
      .subscribe( response => this._someStuff.next(response) )
    );     
  }

  // rest of CRUD implementations
  // ...

  ngOnDestroy(): void {                                                               
    this.httpSubs.unsubscribe();                                        // B3. //
    this._someStuff.complete();                                         // C. //
  }

我标记的东西 (A.-C.) 与我在常见教程和内容中看到的不同.
如果我没有遗漏关于组件、服务以及缺少取消订阅可能会如何导致副作用的一些基本知识……那么我关于是否有必要取消订阅/完成服务的问题是:

I marked things (A.-C.) that are not as I have seen in common tutorials and stuff.
If I don't miss something fundamental about components,services and how missing unsubscriptions might cause side effects... then my questions regarding if unsubscirbe/complete in a service is necessary are:

  1. 我希望现在我们在服务中有订阅,必须在此处实现 OnDestroy (A.),就像我们在组件中所做的那样,以便能够正确取消订阅.我想这甚至是在 root 中提供服务时的情况(因此将作为应用程序的最后一部分被销毁),因为该服务可能会对另一个同样不是的 root 服务产生副作用尚未收集垃圾.
    这是正确的吗?

  1. I expect now that we have the subscription in the service, it is mandatory to implement OnDestroy here (A.), as we do in the components to be able to unsubscribe correctly. I guess this is even the case when the service is provided in root (and therfore will destroyed as one of the last parts of the application), since it might be possible that the service causes side effects on another root service that also isn't garbage collected yet.
    Is this correct?

如果我们不在服务中取消订阅,我们将面临与在组件中不取消订阅时相同的问题.因此添加了B 1 - 3(实际上如何取消订阅的类型并不重要-只有我们这样做的事实-我选择了本机RxJS方法)
这是正确的吗?

If we don't unsubscribe in the servie, we will face the same problems as when we wouldn't unsubscribe in the component. Therefore B 1 - 3 are added (in fact the type how to unsubscribe is not important - only the fact we do - I choose the native RxJS approach)
Is this correct?

(旁注:我知道不取消订阅和手动处理副作用可能是更好的方法 在某些情况下,但让我们忽略这一点,这样问题就不会太宽泛)

(side note: I'm aware that it might be a better approach not to unsubscribe and handle the side effects manually in some cases, but lets ignore this so the question don't gets to broad)

  1. 我不知道 .asObservable 在内部如何工作,但也必须有一些订阅.
    那么像_someStuff(C.)这样的行为科目(或正常科目)是否有必要完成?
  1. I don't know how .asObservable works internally, but there have to be some subscriptions as well.
    So is it necessary to complete the behaviour subjects (or normal subjects) like _someStuff (C.)?

推荐答案

如果 observable(例如:来自 HttpClient)完成,则无需取消订阅.

If an observable(e.g: from HttpClient) completes, there is no need to unsubscribe.

如果 observable 的源完成发出错误,源将自动取消订阅.

If the source of the observable completes or emits an error, the source will be unsubscribed automatically.

例如,当你有这样的事情

For example, when you have something like this

http.get(...)
 .pipe(
  a(),
  b(),
  c(),
 ).subscribe(observer)

幕后发生了一些有趣的事情.首先将observer 转换为subscriber.Subscriber 类扩展了 Subscription,它包含 取消订阅逻辑.

A few interesting things happen behind the scenes. First of all the observer is converted into a subscriber. The Subscriber class extends Subscription, which is holds the unsubscription logic.

链中的每个操作符(abc)都有自己的 Subscriber 实例,因为在第一个 subscribe 之后,他们的运营商返回的每个 observable 也将被订阅(运营商是一个接收 observable 并返回一个 observable 的函数).你最终得到的是一个 Subscriber 链.

Each operator(a, b and c) in the chain there will have its own Subscriber instance, because after the first subscribe, each observable returned by their operator will be subscribed as well(an operator is a function which receives an observable and returns an observable). What you end up with is a chain of Subscribers.

// S{n} -> Subscriber {n} (in the order in which they are created)
http.get(...)
 .pipe(
  a(), // S4
  b(), // S3
  c(), // S2
 ).subscribe(observer) // S1

S1S2 的父(destination),S2S3 的父 等等.

S1 is the parent(destination) of S2, S2 is the parent of S3 and so forth.

HttpClient.get 本质上与

new Observable(subscriber => {
 // Make request

 // On request ready
 subscriber.next(resultOfTheRequest)
 subscriber.complete(); // So here completes!
})

源代码

值得一提的是,在这种情况下,subscriber 参数将是 S4.

What's important to mention is that, in this case, the subscriber parameter will be S4.

subscriber.complete()S4.complete() 相同.发生这种情况时,S4 会将完成通知传递给 S3,后者又将通知传递给 S2 依此类推,直到 S1 收到通知.此时,它将取消订阅.

subscriber.complete() is the same as S4.complete(). When this happens, S4 will pass along the complete notification to S3, which will in turn pass along the notification to S2 and so on until S1 receives the notification. At this point, it will unsubscribe.

当订阅者取消订阅时,其所有后代也将取消订阅.

When a subscriber unsubscribes, all of its descendants will be unsubscribed as well.

因此,取消订阅封闭订阅者(取消订阅后变为封闭)是无害的,但冗余.

So, unsubscribing from a closed subscriber(it becomes closed after unsubscribing) is innocuous, but redundant.

如果在根级别提供服务(例如:providedIn: 'root'),则不会调用其 ngOnDestroy.据我所知,只有在服务被销毁时才会调用它,当您在组件级别提供它时就是这种情况.

If a service is provided at root level(e.g: providedIn: 'root'), its ngOnDestroy will not be called. As far as I've noticed, it will be called only if the service is destroyed, which is the case when you provided it at component level.

这是一个演示,说明了这一点.

Here's a demo which illustrates this.

IMO,如果服务是在根级别提供的,您不必担心取消订阅 observable.但是,如果您的服务是在组件级别提供的,您应该退订.

IMO, if the service is provided at root level, you should not worry about unsubscribing from observables. If, however, your service is provided at component level, you should unsubscribe.

如前几节所述,如果您的 observable 完成,无论您的服务在哪里提供,您都不必担心取消订阅.

As described in the previous sections, if your observables complete, no matter where your service is provided, you should not worry about unsubscribing.

如果您的 observables 没有自行完成(例如 fromEvent)并且服务在根级别未提供strong>,您应该在 ngOnDestroy 中手动取消订阅.

If your observables do not complete by themselves(e.g fromEvent) and the service is not provided at root level, you should manually unsubscribe in ngOnDestroy.

Subject 将维护一个活跃订阅者的列表.当活跃订阅者变为不活跃时,它将从订阅者列表中删除.

A Subject will maintain a list of active subscribers. When an active subscriber becomes, well, inactive, it will be removed from the subscribers list.

例如,如果您在根提供的服务中有一个 BehaviorSubject,您就不必担心丢弃它的引用.

For instance, if you have a BehaviorSubject in a root-provided service, you should not worry about throwing away its references.

实际上,一个更安全的方法是调用 Subject.unsubscribe().Subject.complete() 将向其订阅者发送完整通知,然后清空列表.

Actually, a safer way to do this would be to call Subject.unsubscribe(). Subject.complete() will send the complete notification to its subscribers and then will empty out the list.

这篇关于是否有必要在后端服务中取消订阅/完成以防止副作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆