【问题标题】:Execute function after chain of observable subscriptions completes (Angular 2)在可观察订阅链完成后执行函数(Angular 2)
【发布时间】:2016-07-09 07:04:51
【问题描述】:

我需要执行一个函数,该函数处理由一系列可观察订阅产生的数据,但仅在它完成创建我的对象之后。

这是我的链子:

 getFilters() {
        this.filterSvc.getCamps()
            .subscribe(
                c => {
                    this.filters = c;
                    for (let camp of this.filters) {
                        this.filterSvc.getBuildings(camp.id)
                            .subscribe(
                                b => {
                                    camp.buildings = b;
                                    for (let building of camp.buildings) {
                                        this.filterSvc.getFloors(building.id)
                                            .subscribe(f => {
                                                building.floors = f
                                            });
                                    };
                                });
                    }
                });
           // ONLY DO THIS AFTER THE OBJECT IS HYDRATED
           this.globals.setCampFilters(this.filters);
    }

如您所见,我需要从 getCamps 返回的每个项目创建一个订阅,并从每个结果中创建一个订阅,等等......然后说完,我想执行

setCampFilters(this.filters);

我如何才能等到我的所有营地都有建筑物并且所有这些建筑物都有楼层之后才去填充我的过滤器?

【问题讨论】:

    标签: angularjs typescript angular promise observable


    【解决方案1】:

    我会使用flatMap 运算符和Object.forJoin。这是一个示例:

    getFilters() {
      this.filterSvc.getCamps()
            .flatMap(c => {
              this.filters = c;
              // Observables for all camps
              return Observable.forkJoin(this.filters.map(camp => {
                // Links camp with the result of getBuildings
                return Observable.forkJoin([
                  Observable.of(camp),
                  this.filterSvc.getBuildings(camp.id)
                ]);
              })
            })
            .map(results => {
              // Map results and link buildings with camps
              return results.map(result => {
                let camp = result[0];
                let buildings = result[1];
                camp.buildings = buildings;
                return camp;
              });
            })
            .subscribe(camps => {
              // ...
            ]);
    

    请注意,您可以链接 flatMap 运算符。

    您可能会对这篇文章感兴趣:

    【讨论】:

    • 看起来你正在做一些事情,但是当实现时,执行永远不会到达很多内部代码。即使在几乎所有地方都感到慌乱并设置断点之后,大部分都没有被命中,我也没有收到任何错误,最终我的过滤器永远不会被填充。恕我直言,我想知道您是否只是在为您的博客文章添加点击量,还是您实际上已经考虑过这个问题?
    【解决方案2】:
    window.getFilters = function() {
      this.filterSvc.getCamps()
      .do((c: any) => this.filters = c)
      .flatMap((c: any) => Observable.from(c))   // convert from Observable<Array<Camp>> to Observable<Camp>
      .flatMap((camp: any) => this.filterSvc.getBuildings(camp.id).do(b => camp.buildings = b))
      .flatMap(b => Observable.from(b))
      .flatMap((building: any) => this.filterSvc.getFloors(building.id).do(f => building.floors = f))
      .subscribe(null, null, () => this.globals.setCampFilters(this.filters));
    }
    

    一系列的flatMap将Observable从Observable>变成Observable>,你只需要在它的onComplete事件上调用this.globals.setCampFilters

    编辑:我更新了代码以阻止 Typescript 编译器抱怨不兼容的类型。我在我认为 TS 编译器会抱怨的地方添加 : any,您可以在每个 =&gt; 之前添加 :any

    【讨论】:

    • 您的回答似乎很有希望,但我收到了一系列错误,类似于“类型'{}' 上不存在属性'id'”和“类型'{}' 上不存在属性'建筑物' “而且我无法告诉 TS c 属于“MenuCamp”类型,而建筑物属于“MenuBuilding”类型等......
    • 如果您使用的是 TS,您可以将 c =&gt; camp =&gt; .... 替换为 (c: any) =&gt; (camp: any) =&gt; 。如果你愿意,可以在代码编译成功后尝试将any替换为对应的类型(但我认为没有必要)
    • 下次请考虑添加#typescript标签。如果您使用的是 TS 或 ES6/7,很难从您的代码中分辨出来
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-23
    相关资源
    最近更新 更多