在forkJoin中是否需要取消订阅combineLatest?

huangapple go评论65阅读模式
英文:

Is it necessary to unsubscribe from a combineLatest within a forkJoin?

问题

根据RXJS文档本身,从我理解的角度来看,forkJoin 在完成所有调用后会发出 completed(),这会自动取消订阅,因此不需要手动取消订阅。

但是如果我有一个 combineLatest 正在订阅某些内容,就像这样:

forkJoin({
  xSubject: this.serviceX.on(),
  ySubject: this.serviceY.on(),
  zSubject: this.serviceZ.on(),
  wSubject: this.serviceW.on()
})
.pipe(
  switchMap(({ xSubject, ySubject, zSubject, wSubject }) => {
    return combineLatest([xSubject, ySubject, zSubject, wSubject]);
  })
)
.subscribe(([x, y, z, w]) => {
  console.log(x)
  console.log(y)
  console.log(z)
  console.log(w)
});

在这种情况下,是否有必要取消订阅?如果有必要的话,最佳方式是什么?

英文:

According to the RXJS documentation itself, from what I understood, the forkJoin, when finishing all the calls, it gives completed(), which automatically unsubscribes, with this it is not necessary to manually unsubscribe.

But what if I have a combineLatest that is signing something, like this:

 forkJoin({
      xSubject: this.serviceX.on(),
      ySubject: this.serviceY.on(),
      zSubject: this.serviceZ.on(),
      wSubject: this.serviceW.on()
    })
    .pipe(
      switchMap(({ xSubject, ySubject, zSubject, wSubject }) => {
        return combineLatest([xSubject, ySubject, zSubject, wSubject]);
      })
    )
    .subscribe(([x, y, z, w]) => {
      console.log(x)
      console.log(y)
      console.log(z)
      console.log(w)
    });

Is it necessary to unsubscribe from it? What is the best way in this case, if necessary?

答案1

得分: 1

作为一个经验法则,即使对于像forkJoin和Angular HTTP Observables这样只发出一次并完成的函数,关闭订阅也总是更好的。有多种因素可能导致订阅保持打开。

在你的情况下,内部可观察对象不会自动关闭。所以你需要手动关闭它。请注意,在以下代码片段中,complete仅在close可观察对象发出后才被记录,而使用takeUntil操作符手动关闭了订阅。

const { of, forkJoin, combineLatest, Subject } = rxjs;
const { switchMap, takeUntil } = rxjs.operators;

const obsX = new Subject();
const obsY = new Subject();
const obsZ = new Subject();
const obsW = new Subject();
const close = new Subject();

forkJoin({
  xSubject: of(obsX),
  ySubject: of(obsY),
  zSubject: of(obsZ),
  wSubject: of(obsW),
})
  .pipe(
    switchMap(({ xSubject, ySubject, zSubject, wSubject }) => {
      return combineLatest([xSubject, ySubject, zSubject, wSubject]);
    }),
    takeUntil(close)
  )
  .subscribe({
    next: ([x, y, z, w]) => {
      console.log(x);
      console.log(y);
      console.log(z);
      console.log(w);
    },
    complete: () => console.log('complete'),
  });

setTimeout(() => obsX.next('x value'), 1000);
setTimeout(() => obsY.next('y value'), 2000);
setTimeout(() => obsZ.next('z value'), 3000);
setTimeout(() => obsW.next('w value'), 4000);

setTimeout(() => close.next(), 6000);

希望这对你有所帮助。

英文:

As a rule of thumb, it's always better to close subscriptions, even for functions like forkJoin and Angular HTTP Observables that emits once and completes. There are multiple factors that could leave the subscriptions open.

In your case specifically, the inner observable is not closed automatically. So you'd need to close it. Notice in the following snippet, the complete is only logged after the close observable has emitted, which inturn manually closes the subscription using the takeUntil opeartor.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const { of, forkJoin, combineLatest, Subject } = rxjs;
const { switchMap, takeUntil } = rxjs.operators;

const obsX = new Subject();
const obsY = new Subject();
const obsZ = new Subject();
const obsW = new Subject();
const close = new Subject();

forkJoin({
  xSubject: of(obsX),
  ySubject: of(obsY),
  zSubject: of(obsZ),
  wSubject: of(obsW),
})
  .pipe(
    switchMap(({ xSubject, ySubject, zSubject, wSubject }) =&gt; {
      return combineLatest([xSubject, ySubject, zSubject, wSubject]);
    }),
    takeUntil(close)
  )
  .subscribe({
    next: ([x, y, z, w]) =&gt; {
      console.log(x);
      console.log(y);
      console.log(z);
      console.log(w);
    },
    complete: () =&gt; console.log(&#39;complete&#39;),
  });

setTimeout(() =&gt; obsX.next(&#39;x value&#39;), 1000);
setTimeout(() =&gt; obsY.next(&#39;y value&#39;), 2000);
setTimeout(() =&gt; obsZ.next(&#39;z value&#39;), 3000);
setTimeout(() =&gt; obsW.next(&#39;w value&#39;), 4000);

setTimeout(() =&gt; close.next(), 6000);

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

答案2

得分: 0

在使用像switchMap这样的操作符时,非常重要区分外部订阅(在你的代码示例中是forkJoin)和内部订阅(在你的情况下是combineLatest)。内部示例中的订阅是由外部订阅触发的,也就是说,每当forkJoin发出某些内容时,switchMap就会被触发,并且它将订阅combineLatest。因此,仅仅因为外部订阅已经完成或取消订阅,并不会影响内部订阅,所以是的,你需要取消订阅。

Nicholas Jamieson也在他的博客文章中谈到了这一点,链接在这里:https://ncjamieson.com/avoiding-takeuntil-leaks/

英文:

When using operators like switchMap it is very important to differentiate between outer subscription (in your code example forkJoin) and the inner subscription (in your case combineLatest). The subscription on the inner example is triggered by the outer subscription, aka. whenever forkJoin emits something, switchMap is triggered and it will subscribe to combineLatest. Therefore just because the outer subscription is complete or even unsubscribe, that does not affect the inner subscription, so yes you would need to unsubscribe.

Nicholas Jamieson also talk about this in his blog post https://ncjamieson.com/avoiding-takeuntil-leaks/

huangapple
  • 本文由 发表于 2023年7月17日 21:22:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704906.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定