等待多个可观察对象(并行)以 Angular 效果完成

waiting for multiple observables (in parallel) to complete in an Angular effect

我正在编写一个效果,在完成之前需要几个单独的服务调用的结果(return作为可观察对象)。第一个服务调用必须在 3 个以上(可以是异步的)之前完成,然后我构建我的操作列表。我让它工作,但感觉它是不必要的嵌套。

@Effect() loadMyData$: Observable<any> = this.actions$.pipe(
    ofType(fromMyData.LOAD_DATA),
    switchMap((action: fromMyData.Load) => {
        return this.dataService.openMyData(action.payload).pipe(
            switchMap(() => this.dataService.getShapes()),
            switchMap(shapeData => {
                return this.dataService.getCircles().pipe(
                    switchMap((circleData) => {
                        return this.dataService.getTriangles().pipe(
                            switchMap((triangleData) => {
                                const flatMyDataWithReferences: any = {
                                    triangleName: triangleData.name,
                                    shapeName: shapeData.name
                                };
                                return [
                                    new fromShapes.Load(),
                                    new fromBalls.Load(),
                                    new fromCircles.LoadListSuccess(
                                        this.organizeCircles(circleData)),
                                    new fromMyData.LoadSuccess(flatMyDataWithReferences),
                                    new fromUi.AddMessage({
                                        type: MessageType.Success, message: 'Successfully loaded my data ' +
                                            shapeData.name +
                                            ' from: ' + action.payload
                                    })
                                ];
                            }),
                            catchError((err) => this.myDataLoadErrorEvents(err))
                        );
                    }),
                    catchError((err) => this.myDataLoadErrorEvents(err))
                );
            }),
            catchError((err) => this.myDataLoadErrorEvents(err))
        );
    }),
    catchError((err) => this.myDataLoadErrorEvents(err))
);

基本上,在我的示例代码中,一旦我调用 return 调用 dataService.openMyData 调用,我想并行执行这些调用:

一旦它们全部完成,我想在我的动作数组中使用它们的 returned 数据放入 return [new etc...] 来结束效果。

希望有人有比这个丑陋(和不必要的)缩进地狱更优雅的方式来处理 3 个中间服务调用...

我环顾四周,发现有些人正在使用 forkJoin 来等待多个 observable 的结果,但他们似乎无法将那个 forkJoin observable 的结果用作 return 来操作.例如,下面这段代码告诉我,我最终没有创建一个可观察对象,这是我的效果所需要的..

@Effect() loadMyData$: Observable<any> = this.actions$.pipe(
    ofType(fromMyData.LOAD_DATA),
    switchMap((action: fromMyData.Load) => {
        return this.dataService.openMyData(action.payload).pipe(
            switchMap(() => {
                return forkJoin(
                        this.dataService.getShapes(),
                        this.dataService.getCircles(),
                        this.dataService.getTriangles()
                    ).pipe(
                        map(joinResult => {
                            const [shapeData, circleData, triangleData] = joinResult;
                            const flatMyDataWithReferences: any = {
                                triangleName: triangleData.name,
                                shapeName: shapeData.name
                            };
                            return [
                                new fromShapes.Load(),
                                new fromBalls.Load(),
                                new fromCircles.LoadListSuccess(
                                    this.organizeCircles(circleData)),
                                new fromMyData.LoadSuccess(flatMyDataWithReferences),
                                new fromUi.AddMessage({
                                    type: MessageType.Success, message: 'Successfully loaded my data ' +
                                        shapeData.name +
                                        ' from: ' + action.payload
                                })
                            ];
                        })
                    );
            }),
            catchError((err) => this.myDataLoadErrorEvents(err))
        );
    }),
    catchError((err) => this.myDataLoadErrorEvents(err))
);

使用 forkJoin 你可以使用结果,不知道:

forkJoin(
    this.dataService.getShapes(),
    this.dataService.getCircles(),
    this.dataService.getTriangles()
).pipe(
    map(res => {
         // Here you can use the result. That result is an array of last emitted value for each observable. The order is the same that you declare in observable array.
        const [shapes, circles, triangles] = res;
     })
);

需要考虑的一件重要事情是,如果其中一个请求失败,您将无法收到任何值,应该正确地捕获它。

这里可以看到更多信息forkJoin

1。使用 forkJoin 等待多个 Observable 完成

事实证明,我的问题需要两个重要的解决方案才能使其作为一个适当的可观察动作流发挥作用,这正是效果作为 return 所需要的。第一部分是@llsanchez 的回应——使用 return forkJoin().pipe(map(response => response.stuff)); 形式完成效果,forkJoinpipemap 是正确的顺序。

2。根据您的需要,在您的 Effect

中使用 mapmergeMap 到 Return Action Observable

为了在具有多个结果操作的效果中正常工作(我的示例有),您必须将 map 运算符替换为 mergeMap,如下所示:

@Effect() loadMyData$: Observable<any> = this.actions$.pipe(
    ofType(fromMyData.LOAD_DATA),
    switchMap((action: fromMyData.Load) => {
        return this.dataService.openMyData(action.payload).pipe(
            switchMap(() => {
                return forkJoin(
                        this.dataService.getShapes(),
                        this.dataService.getCircles(),
                        this.dataService.getTriangles()
                    ).pipe(
                        mergeMap(joinResult => {
                            const [shapeData, circleData, triangleData] = joinResult;
                            const flatMyDataWithReferences: any = {
                                triangleName: triangleData.name,
                                shapeName: shapeData.name
                            };
                            return [
                                new fromShapes.Load(),
                                new fromBalls.Load(),
                                new fromCircles.LoadListSuccess(
                                    this.organizeCircles(circleData)),
                                new fromMyData.LoadSuccess(flatMyDataWithReferences),
                                new fromUi.AddMessage({
                                    type: MessageType.Success, message: 'Successfully loaded my data ' +
                                        shapeData.name +
                                        ' from: ' + action.payload
                                })
                            ];
                        })
                    );
            }),
            catchError((err) => this.myDataLoadErrorEvents(err))
        );
    }),
    catchError((err) => this.myDataLoadErrorEvents(err))
);

总而言之,要return一个正确的动作流:

到 return 效果中的单个动作使用 map,如以下形式:

return forkJoin().pipe(map(response => response.action))

到 return 效果中的多个操作使用 mergeMap,如以下形式:

return forkJoin().pipe(mergeMap(response => [response.action1, response.action2]))