等待可观察的用forkJoin在foreach内部订阅

时间:2018-11-26 11:08:29

标签: javascript angular typescript rxjs observable

我正在尝试在我的名为Conventions的组件中填充一个数组,这是一个约定数组。

每个组织都有一份合同清单,每份合同都有一个约定编号,有了这个编号,我便得到了约定。

我使用getOrganizationForUser获取当前组织,然后获取合同列表。 然后,我使用合同中的约定ID来调用第二个API以获取约定。

当前,我的代码如下所示:

public getOrganizationForUser(): Observable<Organization> {
        return this.httpClient
            .get<Organization>(`${c.serviceBaseUrl.sp}/organizationByUser`)
            .pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
    }

 public getById(id: number) {
        return this.httpClient
            .get<Convention>(`${c.serviceBaseUrl.sp}/conventions/` + id)
            .pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
    }

ngOnInit() {
    this.OrganizationService.getOrganizationForUser().subscribe((organization: Organization) => {
        organization.contracts.forEach((contract) => {
            this.conventionService.getById(contract.conventionId).subscribe((convention: Convention) => {
                this.conventions.push(convention);
            })
        })
   })
}

我知道我可以创建一个可观察对象数组,并使用Observable.forkJoin()等待所有这些异步调用完成,但是我希望能够定义订阅回调 每个调用的函数,因为我需要对该过程的引用。关于如何解决这个问题有什么想法吗?

我尝试过使用此功能,但总是可以理解

getTasksForEachProcess(): Observable<Array<any>> {
    let tasksObservables = this.organizationService.getOrganizationForUser().pipe(map((organization: Organization) => {
        organization.contractOrganizations.map(contract => {
            return this.conventionService.getById(contract.conventionId).subscribe(convention =>
                this.conventions.push(convention)
            )
        });
    })
    );
    return forkJoin(tasksObservables);
};

ngOnInit() {
    this.getTasksForEachProcess().subscribe(item => {
        console.log(item);
    }
}

2 个答案:

答案 0 :(得分:0)

首先,由于我不了解您的意思

,我不确定您真正想要实现的目标
  

我希望能够为每个定义订阅回调函数   通话,因为我需要参考流程

无论如何,在您所描述的情况下,我会做这样的事情

public getOrganizationForUser(): Observable<Organization> {
        return this.httpClient
            .get<Organization>(`${c.serviceBaseUrl.sp}/organizationByUser`)
            .pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
    }

 public getById(id: number) {
        return this.httpClient
            .get<Convention>(`${c.serviceBaseUrl.sp}/conventions/` + id)
            .pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
    }

ngOnInit() {
    const obsOfObservables = this.OrganizationService.getOrganizationForUser()
    .pipe(
       map(organization => organization.contracts),
       map(contracts => contracts.map(contract => this.conventionService.getById(contract.conventionId)))
    );

    obsOfObservables
    .pipe(
       switchMap(conventionObservables => forkJoin(conventionObservables))
    )
    .subscribe(
       conventions => { // do stuff with conventions }
    )
}

这里的要点如下。

通过getOrganizationForUser(),您将获得一个发出组织的Observable。您要做的第一件事就是使用第一个map运算符将Observable发出的对象转换为合同数组。

第二个map运算符将合同数组转换为约定的可观察数组。为了执行此转换,我们在Observable的map运算符内使用Array的map方法。这可能有点令人困惑,但是值得理解。

如果我们在这里停止,那么我们拥有的是obsOfObservables,即发出一个Observable数组的Observable。

然后,我们将obsOfObservables发出的Observable数组传递给forkJoin函数,该函数本身返回一个Observable。由于我们实际上对forkJoin返回的Observable通知的内容感兴趣,即我们对约定感兴趣,因此我们需要将 switch 从第一个Observable切换到第二个Observable,通过switchMap运算符完成。

最终结果是一个Observable,它返回约定数组。考虑添加常数obsOfObservables作为澄清原因的尝试,并且完全没有必要(如Barney Panofsky所说)。

我还没有模拟整个过程,所以我希望我没有插入错误,但是或多或少这是我在这种情况下要使用的思考过程。

最后一点,当您在subscribe中有subscibe时,通常要多加怀疑。

答案 1 :(得分:0)

我同意Picci通过思考来回答的逻辑。这与他的提议略有不同,尽管像他一样,我并未对此进行严格测试,并且可能会有一些错误。

这样做的逻辑是,您最终想要的是一个约定数组,而从“ observables”产生一个数组就是“ zip”的作用。流程如下:

  • 创建一个组织,然后使用rxjs'organization.contractsfrom数组中创建可观察对象流。
  • 该流中的每个项目都是一个合同,然后将其转换为(使用map的约定,并使用contract.conventionId属性使用API​​查找。
  • 最终得到的所有可观察到的约定流最终将通过包装的zip转换回数组,并作为可订阅的可观察对象交付,以生成所需的约定数组。

代码如下:

ngOnInit() {
    zip( this.OrganizationService.getOrganizationForUser()
        .pipe(
            map((organization: Organization) => 
                from(organization.contracts).pipe(
                    map(contract => this.conventionService.getById(contract.conventionId))
                )
            )
        )
    )
    .subscribe((conventions: Convention[]) => this.conventions = conventions)
}