ожидание нескольких наблюдаемых (параллельно) для завершения в угловом эффекте - PullRequest
0 голосов
/ 11 марта 2019

Я пишу эффект, который требует результатов нескольких отдельных вызовов службы (возвращаемых в виде наблюдаемых) до его завершения. Первый сервисный вызов должен завершиться еще до 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))
);

По сути, в моем примере кода здесь, после того, как я позвонил и вернулся из вызова dataService.openMyData, я бы хотел сделать эти вызовы параллельно:

  • dataService.getShapes
  • dataService.getCircles
  • dataService.getTriangles

После того, как все они завершатся, я хотел бы использовать их возвращенные данные в моем массиве действий, чтобы вставить return [new etc...], чтобы обернуть эффект.

надеясь, что у кого-то есть более изящный способ приблизиться к трем вызовам службы посредника, чем этот безобразный (и ненужный) адский отступ ...

Я оглянулся и обнаружил, что некоторые люди используют forkJoin для ожидания результата нескольких наблюдаемых, но, похоже, они не могут использовать результат этого наблюдаемого forkJoin в качестве возврата для работы. Например, этот код ниже говорит мне, что я не создаю наблюдаемое в конце, чего требует мой эффект ..

@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))
);

Ответы [ 2 ]

2 голосов
/ 12 марта 2019

С помощью 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 голос
/ 12 марта 2019

1.Используйте forkJoin, чтобы дождаться завершения нескольких наблюдаемых

Оказывается, мой вопрос требовал двух важных решений, чтобы заставить его работать как правильный поток наблюдаемых действий, а это то, что эффект требует в качестве возврата.Первая часть - это отклик @llsanchez - используется форма return forkJoin().pipe(map(response => response.stuff)); для завершения эффекта с forkJoin, pipe и map - правильная последовательность.

2.В зависимости от ваших потребностей, используйте map или mergeMap для возврата наблюдаемых действий в вашем эффекте

Чтобы вещи правильно работали в эффекте с несколькими результирующими действиями (как в моем примере), вы должнызамените оператор 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))
);

В итоге, чтобы получить правильный поток действий в эффекте:

Чтобы вернуть одно действие в эффектеиспользуйте map, как в форме:

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

Для возврата нескольких действий в эффекте используйте mergeMap, как в форме:

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