如何在最后一个观察者取消订阅时阻止AsyncSubject完成

时间:2017-07-14 07:16:49

标签: javascript rxjs

当最后一位主题观察者取消订阅该主题时,AsyncSubject变为可观察的。这是the quote

  

一旦完成,就完成了。受试者在他们之后不能重复使用   取消订阅,已完成或已出错。

以下是演示:

const ofObservable = Rx.Observable.of(1, 2, 3);
const subject = new Rx.AsyncSubject();

ofObservable.subscribe(subject);

subject.subscribe((v) => {
    console.log(v);
});

subject.unsubscribe((v) => {
    console.log(v);
});

// here I'll get the error "object unsubscribed"
subject.subscribe((v) => {
    console.log(v);
});

如何防止主题完成?

share运算符:

  

在RxJS 5中,运算符share()生成一个热的refCounted observable   可以在失败时重试,也可以在成功时重复。因为   受试者一旦出错,完成或以其他方式不能重复使用   取消订阅后,share() 运营商会将已发布的主题回收   启用重新订阅以生成observable。

这就是我要找的东西。但share创建了一个主题,我需要AsyncSubject

1 个答案:

答案 0 :(得分:8)

问题出在这一行:

subject.unsubscribe((v) => {
    console.log(v);
});

Subject implements ISubscription;这意味着它具有unsubscribe方法和closed属性。它的implementation of unsubscribe如下:

unsubscribe() {
  this.isStopped = true;
  this.closed = true;
  this.observers = null;
}

这有些残酷。从本质上讲,它会切断与主题的任何订阅者的所有通信,而不会取消订阅。同样,它也不会取消订阅主题本身,也不会取消订阅可能发生的任何观察。 (它还将主题标记为已关闭/已停止,这是您出错的原因。)

鉴于它没有实际执行任何取消订阅,它应该如何使用尚不清楚。 this test

的说明
it('should disallow new subscriber once subject has been disposed', () => {

表明它可能是某种来自RxJS 4的宿醉 - 其中取消订阅被称为处置。

不管它存在的原因是什么,我建议永远不要叫它。举例来说,请看这个片段:



const source = Rx.Observable
  .interval(200)
  .take(5)
  .do(value => console.log(`source: ${value}`));

const subject = new Rx.Subject();
source.subscribe(subject);

const subscription = subject
  .switchMap(() => Rx.Observable
    .interval(200)
    .take(5)
    .delay(500))
  .subscribe(value => console.log(`subscription: ${value}`));

.as-console-wrapper { max-height: 100% !important; top: 0; }

<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
&#13;
&#13;
&#13;

它将主题订阅到源可观察对象,然后订阅由该主题组成的可观察对象。

如果在主题上调用unsubscribe,则会出现一些问题:

  • 主题人对订阅源的订阅未被取消订阅,并且当来源尝试调用主题next方法时会产生错误;和
  • 订阅由该主题组成的可观察对象未被取消订阅,因此interval switchMap unsubscribe中的const source = Rx.Observable .interval(200) .take(5) .do(value => console.log(`source: ${value}`)); const subject = new Rx.Subject(); source.subscribe(subject); const subscription = subject .switchMap(() => Rx.Observable .interval(200) .take(5) .delay(500)) .subscribe(value => console.log(`subscription: ${value}`)); setTimeout(() => { console.log("subject.unsubscribe()"); subject.unsubscribe(); }, 700);观察点会在.as-console-wrapper { max-height: 100% !important; top: 0; }来电后不断发出。

尝试一下:

&#13;
&#13;
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
&#13;
unsubscribe
&#13;
Subject
&#13;
&#13;
&#13;

这似乎都不是理想的行为,因此在Subscription上调用subscribe是值得避免的。

相反,代码段中的代码应使用const subscription = subject.subscribe((v) => { console.log(v); }); subscription.unsubscribe(); 调用返回的next取消订阅:

unsubscribe

在写完这个答案之后,我发现了来自following commentBen Lesh,这符合我的理论,它与处理该主题有关:

  

如果您希望主题在<div id="modal" style="background-color: #000;"> <!--MODAL----> <span id="close">&times;</span> </div> <img id="image" src="agave.jpg"/> <!--Click to display modal--> 完成后对其进行大声和愤怒的错误,您可以直接在主题实例上调用var img = document.getElementById("image"); function displayModal(event){ var modal = document.getElementById("modal"); if(modal.style.display == "none"){ modal.style.display = "block"; } else{modal.style.display = "none";} } img.addEventListener("click", displayModal(event));