如果可观察对象正在进行中,请订阅它;否则,请重新启动它

时间:2019-01-10 20:47:13

标签: angular typescript rxjs

如果我的观察对象需要很长时间才能执行。我们将其称为longObservable,其类型为Observable,执行时间为5秒钟,每次执行时,它仅发出一次新字符串,然后完成。

longObservable(): Subject<string> {
    return timer(5000).pipe{
        map(() => randomString())
    }
}

和其他一些页面多次调用它。如果正在处理中,我只想继续执行一个过程。如果完成,我想重新开始。

longObservable.subscribe() // Immediate, start the timer

,这将在两秒钟后运行:

longObservable.subscribe() // Get the same string as
                            //  the above subscription in 3 seconds.

这会在20秒后运行

longObservable.subscribe() // Start a new iteration and wait
                            // 5 seconds, get a new string.

我认为第二个订阅很容易,它将按我想要的方式工作。这是我遇到的第三个问题。由于longObservable完成,它将立即发出与其他两个相同的值。

这用于设备上的地理位置。我想请求一个新位置,但是如果已经有一个请求在进行中,请使用该结果。

编辑:将可观察对象更改为多播对象,删除了take(1)。

Edit2:https://stackblitz.com/edit/angular-venpk4是我想要的工作示例。我希望在没有timerRunning变量和RxJS运算符的情况下完成此操作。它位于hello组件下,并打印到控制台。

3 个答案:

答案 0 :(得分:2)

棘手的问题。这是我在StackBlitz中的解决方案。 share()运算符是几个关键点,它可以将可观察对象有效地转换为主题,而不必显式声明主题。但是,您需要在旧订阅完成后用新订阅创建一个新主题,因此我创建了一个工厂函数来返回现有的可共享Observable(如果longObservable()仍在进行中)或生成一个新主题一个。

以下是StackBlitz的重要内容:

let inProgress: boolean = false;

function longObservable(): Observable<string> {
    return timer(5000).pipe(
        map(() => randomString()),
        tap(() => inProgress = false),
        share()
    )
}

let obs$: Observable<string>;

function getLongObs(): Observable<string> {
    if (inProgress) {
        return obs$
    } else {
        inProgress = true;
        obs$ = longObservable();
        return obs$;
    }
}

console.log('initiate first subscribe');
getLongObs().subscribe(
    rand => console.log(`First subscribe returned ${rand}`)
);

setTimeout(() => {
    console.log('initiate second subscribe');
    getLongObs().subscribe(
        rand => console.log(`Second subscribe returned ${rand}`)
    );
}, 2000);

setTimeout(() => {
    console.log('initiate third subscribe');
    getLongObs().subscribe(
        rand => console.log(`Third subscribe returned ${rand}`)
    );
}, 7000)

我希望这会有所帮助!

答案 1 :(得分:1)

如示例所示,您的方法将返回新的Observable实例,该实例在您每次订阅时都会创建。我认为在您的服务中您可以创建一个属性,该属性将存储您的可观察值。将此属性设置为BehaviorSubject可能更好。您也可以在任何地方订阅此属性。因此,每个订阅都将属于相同的Observable实例。

答案 2 :(得分:1)

我认为您想要的是share()管道。像这样的作品:

    export class AppComponent {
        private _longObservable: Observable<string> = null

        constructor() {
            this._longObservable = timer(5000).pipe(
                // This will show us when timer emits a value which will prove that the
                // first two subscriptions below are sharing the same "execution"
                // of the observable.
                tap(() => console.log("Timer Triggered!")), 
                map(() => randomString()),
                share()
            );
        }

        ngOnInit() {
            // These two will share the observable,
            // since long observable hasn't completed by the time the second
            // subscription is triggered.
            this._longObservable.subscribe(console.log);
            setTimeout(() => this._longObservable.subscribe(console.log), 2000);

            // This subscription occurs after the 5 sec.
            // Since timer is a cold observable, this will trigger it to run again.
            setTimeout(() => this._longObservable.subscribe(console.log), 7000);
        }


    }

输出:

Timer Triggered!
randomString1
randomString1
Timer Triggered!
randomString2

如果您不熟悉这种区别,这里有一篇关于热和冷可观察物的区别的文章:https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339

Angular的Http请求和timer(5000)都是冷观测对象。

此处是指向有关共享管道的一些信息的链接:https://www.learnrxjs.io/operators/multicasting/share.html