首页 > 解决方案 > 如果 rxjs 仍在进行数据加载,如何返回数据或等待

问题描述

我有一个服务,它使用 Observable 在它的构造函数中加载一些数据。然后在稍后的某个时间,可以使用 getter 检索数据。如果数据存在,它应该立即返回数据。或者,如果仍在进行中,请等待加载完成。我想出了以下示例(代码在 Typescript 中):

class MyService {
    private loadedEvent = new Subject<any>();
    private loaded = false;
    private data: any;

    constructor(
        private dataService: DataService
    ) {
        this.dataService.loadData().subscribe(data => {
            this.data = data;
            this.loaded = true;
            this.loadedEvent.next(null);
        });
    }

    public getDataOrWait(): Observable<any> {
        if (this.loaded) { // A
            return of(this.data);
        }
        return new Observable((observer: Observer<any>) => {
            const subscription = this.loadedEvent.subscribe(() => { // B
                observer.next(this.data);
                observer.complete();
                subscription.unsubscribe();
            });
        });
    }
}

有没有更简单的方法来做到这一点?这一定是一种常见的模式。

此外,我认为如果在标记为 A 和 B 的行之间的某处执行加载完成,则存在竞争条件(我不确定这里是否涉及线程 - 但是数据是异步加载的)。

标签: angulartypescriptrxjs

解决方案


您所要做的就是使用shareReplay()运算符:

class MyService {
    public data$: Observable<any>;
    public loaded$: Observable<boolean>;

    constructor(private dataService: DataService) {
        this.data$ = this.dataService.loadData().pipe(
            shareReplay(1);
        );
        this.loaded$ = this.data$.pipe(
           mapTo(true),
           startWith(false)
        );
    }
}

shareReplay运算符是一个多播运算符,它将向所有订阅者发出相同的先前值。订阅者将等到第一个值可用。

然后,您可以使用它data$来创建一个loaded$可观察的对象,该对象将一直发出false直到data$最终发出一个值,然后它总是会true在值准备好时发出。

或者,您可以在数据准备好之前data$发出null数据,然后发出数据。下游有一些合乎逻辑的好处,允许您在数据准备好与否时创建新的 observables。

        this.data$ = this.dataService.loadData().pipe(
            startWith(null),
            shareReplay(1);
        );

您必须调用myService.data$.subscribe()触发流的第一次读取以使数据准备好。您可以在构造函数中执行此操作,但请记住,Angular 在首次使用之前不会创建服务。如果您希望快速加载数据,请在路由中使用解析器或将服务注入NgModule构造函数并在那里订阅。


推荐阅读