rxjs5:延迟取消订阅共享的observable

时间:2016-12-07 06:45:23

标签: typescript rxjs5

我有一个可观察的创建成本很高,所以我shared它。但是,在某些情况下,所有订阅者都会取消订阅,然后立即(或在短暂延迟之后)订阅新订阅者。

实际的observable太复杂了,无法复制,但为了论证:

const heavyObservable = Rx.Observable.create((observer) => {
    console.log('I am expensive, avoid hitting this code');

    return Rx.Observable
            .interval(500) // these updates are cheap though!
            .subscribe(observer)
                .add(() => {
                    console.log('Cache has been destroyed, will have to be rebuild on next call');
                });
});

我不想点击创建此观察所涉及的昂贵代码。我想延迟断开,直到 n ms之后。有没有办法做到这一点?

const sharedObservable = heavyObservable
    .publish()
    // ideally I'm looking for a way to get refCount to wait for new 
    // subscribers for n ms before unsubscribing when refcount === 0
    .refCount(); 

// calling subscribe here invokes heavyObservable which can take a bit of time
const subscription1 = sharedObservable.subscribe();
// log: I am expensive, avoid hitting this code

// second call is "free" - the underlying observable is reused
const subscription2 = sharedObservable.subscribe();

subscription1.unsubscribe();
subscription2.unsubscribe();

// calling subscribe again here invokes heavyObservable over again
const subscription3 = sharedObservable.subscribe();
// log: I am expensive, avoid hitting this code

2 个答案:

答案 0 :(得分:0)

如果没有完全取消订阅,则不会发出新数据(除非在流的开头有触发器,这在您的问题中不明显)。 - 您的案例中的subscription1subscription2应该会收到相同的值。 如果这是设计的,那么你可以简单地不使用refCount(),而只是发布然后做sharedObservable.connect(),在这种情况下,它总是会#34; hot"。 另一个选项可能是publishReplay(1)而不是publish()

在任何情况下,你的情况听起来有点奇怪,并且最有可能通过改变数据流的一般架构来解决 - 但是如果不知道真正的用例,很难说rxjs操作会是什么在这里是最好的。

答案 1 :(得分:0)

试图解决这个问题。下面的函数包装提供的ConnectableObservable source并维护一个refCount订阅者。当第一个订阅者订阅时,它会调用connect(),然后当setTimeout ms之后,最后一个订阅者从unsubscribes取消订阅调用sourcedelay

理想情况下,我宁愿修改现有的refCount可观察对象,但我并不理解这些代码是诚实的。

不确定这是否涵盖所有可能的边缘情况或是否会产生意外的副作用。

Plunker:https://jsbin.com/wafahusitu/edit?js,console

function refCountWithUnsubscriptionDelay<T>(source: Rx.ConnectableObservable<T>, delay: number): Rx.Observable<T> {

    const refCount = 0;
    const sub;
    let timeoutRef;

    return Rx.Observable.create((observer: Rx.Observer<T>) => {
        refCount++;
        if (timeoutRef) {
            clearTimeout(timeoutRef);
        }
        console.log('refCount = ' + refCount);
        if (!sub) {
            // connect on first call
            sub = source.connect();
        }

        return source.subscribe(observer)
                .add(function () {
                    refCount --;
                    if (refCount <= 0) {
                        // trigger delayed unsubscription if there are no listeners
                        timeoutRef = setTimeout(() => {
                            // don't unsubscribe if new listeners have subscribed
                            if (refCount <= 0) {
                                console.log('unsub');
                                sub.unsubscribe();
                                sub = undefined;
                                timeoutRef = undefined;
                            }
                        }, delay);
                    }
                });
    })
}