【问题标题】:How to wait for 2 Actions in @ngrx/effects如何在@ngrx/effects 中等待 2 个动作
【发布时间】:2017-11-19 11:17:48
【问题描述】:

可以像 Promise.all 这样的等待两个动作吗?示例:

@Effect()
pulic addUser() {
   return this.actions$.ofType(user.ADD)
      .switchMap(() => {
         return this.userService.add();
      })
      .map(() => {
         return new user.AddSuccessAction();
      });
}

@Effect()
pulic addUserOptions() {
   return this.actions$.ofType(userOptions.ADD)
      .switchMap(() => {
         return this.userOptionsService.add();
      })
      .map(() => {
         return new userOptions.AddSuccessAction();
      });
}

@Effect()
public complete() {
   return this.actions$.ofType(user.ADD_SUCCESS, userOptions.ADD_SUCCESS)
      // how to make it works like Promise.all ?
      .switchMap(() => {
         return this.statisticService.add();
      })
      .map(() => {
         return new account.CompleteAction();
      });
}

更新 我想要实现的是与 Promise.all 类似的行为。如何并行调度两个效果,等到所有效果都解决后,再调度第三个动作。像https://redux-saga.js.org/docs/advanced/RunningTasksInParallel.html 这样的东西很明显:

Promise.all([fetch1, fetch2]).then(fetch3);

ngrx/effects 有可能吗?还是ngrx/effects中的错误方式?

回答

您可以使用几个选项:

1) 不要使用通用操作。

遵循 Myke Ryan 演示中的这些规则:https://youtu.be/JmnsEvoy-gY

优点:更容易调试

缺点:大量的样板和操作

2) 使用带有嵌套动作的复杂流。

查看这篇文章:https://bertrandg.github.io/ngrx-effects-complex-stream-with-nested-actions/

以下是两个操作的简单示例:

@Effect()
public someAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.SOME_ACTION),
        map((action: actions.SomeAction) => action.payload),
        mergeMap((payload) => {
            const firstActionSuccess$ = this.actions$.pipe(
                ofType(actions.FIRST_ACTION_SUCCESS),
                takeUntil(this.actions$.pipe(ofType(actions.FIRST_ACTION_FAIL))),
                first(),
            );

            const secondActionsSuccess$ = this.actions$.pipe(
                ofType(actions.SECOND_ACTION_SUCCESS),
                takeUntil(this.actions$.pipe(ofType(actions.SECOND_ACTION_FAIL))),
                first(),
            );

            const result$ = forkJoin(firstActionSuccess$, secondActionsSuccess$).pipe(
                first(),
            )
                .subscribe(() => {
                    // do something
                });

            return [
                new actions.FirstAction(),
                new actions.SecondAction(),
            ];
        }),
    );
}

优点:你可以实现你想要的

缺点:复杂的流太复杂,无法支持 :) 看起来很丑,可能很快就会变成地狱,observables 在成功或失败操作之前不会取消订阅,这意味着理论上任何第三方动作可以向这些可观察对象发出信号。

3) 使用聚合器模式。

查看 Victor Savkin 关于 NgRx 的状态管理模式和最佳实践的演讲:https://www.youtube.com/watch?v=vX2vG0o-rpM

这是一个简单的例子:

首先,您需要使用相关性 ID 参数创建操作。 CorrelationId 应该是 uniq,例如它可能是一些 guid。您将在操作链中使用此 ID 来识别您的操作。

export class SomeAction implements Action {
    public readonly type = SOME_ACTION;

    constructor(public readonly correlationId?: string | number) { }
    // if you need payload, then make correlationId as a second argument
    // constructor(public readonly payload: any, public readonly correlationId?: string | number) { }
}

export class SomeActionSuccess implements Action {
    public readonly type = SOME_ACTION_SUCCESS;

    constructor(public readonly correlationId?: string | number) { }
}

export class FirstAction implements Action {
    public readonly type = FIRST_ACTION;

    constructor(public readonly correlationId?: string | number) { }
}

export class FirstActionSuccess implements Action {
    public readonly type = FIRST_ACTION_SUCCESS;

    constructor(public readonly correlationId?: string | number) { }
}

// the same actions for SecondAction and ResultAction

那么我们的效果:

@Effect()
public someAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.SOME_ACTION),
        mergeMap((action: actions.SomeAction) => {
            return [
                new actions.FirstAction(action.corelationId),
                new actions.SecondAction(action.corelationId),
            ];
        }),
    );
}

@Effect()
public firstAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.FIRST_ACTION),
        switchMap((action: actions.FirstAction) => {
            // something
            ...map(() => new actions.FirstActionSuccess(action.correlationId));
        }),
    );
}
// the same for secondAction

@Effect()
public resultAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.SOME_ACTION),
        switchMap((action: actions.SomeAction) => {
            const firstActionSuccess$ = this.actions$.pipe(
                ofType(actions.FIRST_ACTION_SUCCESS),
                filter((t: actions.FirstActionSuccess) => t.correlationId === action.correlationId),
                first(),
            );

            const secondActionsSuccess$ = this.actions$.pipe(
                ofType(actions.SECOND_ACTION_SUCCESS),
                filter((t: actions.SecondActionSuccess) => t.correlationId === action.correlationId),
                first(),
            );

            return zip(firstActionSuccess$, secondActionsSuccess$).pipe(
                map(() => new actions.resultSuccessAction()),
            )
        }),
    );
}

优点:与第 2 点相同,但没有第三方操作。

缺点:与第 1 点和第 2 点相同

4) 不要对 API 使用效果。使用模拟效果但返回 Observable 的良好旧服务。

为您服务:

public dispatchFirstAction(): Observable<void> {
    this.store.dispatch(new actions.FirstAction(filter));

    return this.service.someCoolMethod().pipe(
        map((data) => this.store.dispatch(new actions.FirstActionSuccess(data))),
        catchError((error) => {
            this.store.dispatch(new actions.FirstActionFail());

            return Observable.throw(error);
        }),
    );
}

所以你以后可以在任何地方组合它,比如:

const result1$ = this.service.dispatchFirstAction();
const result2$ = this.service.dispatchSecondAction();

forkJoin(result1$, result2$).subscribe();

5) 使用 ngxs:https://github.com/ngxs/store

优点:样板更少,这感觉像是有棱角的东西,它增长很快

缺点:功能比ngrx少

【问题讨论】:

  • 你的问题不清楚。你能提供更多细节吗?
  • 我正在努力实现同样的目标。你能做到吗?我还查看了使用 zip 运算符的视频,但我无法达到结果
  • 嗨@KaranGarg。我更新了我的答案。
  • 您好,我看到您已在问题正文中添加了一个答案。最好创建一个自己的答案,因为这就是本网站的工作方式。
  • “使用带有嵌套动作的复杂流”,我不认为它是一个“非常复杂”的流,它非常适合我的需要,其他人不喜欢我:1:我不能维护所有重复的内容,没有真正的“好”理由。 2:???? 3:我不想添加需要管理其生命周期的相关逻辑。 4 和 5:我非常喜欢 NgRx。非常感谢@E.Efimov!

标签: angular typescript ngrx ngrx-effects ngrx-store


【解决方案1】:

使用Observable.combineLatest 对我有用。

@Effect()
  complete$ = this.actions$.ofType<Action1>(ACTION1).combineLatest(this.actions$.ofType<Action2>(ACTION2),
    (action1, action2) => {

      return new Action3();
    }
  ).take(1);

take(1) 只会调度 Action3() 一次。

【讨论】:

  • 我认为你需要一个管道在那里语法不正确。这样就完成了效果不行吗?
  • 旧版本的 rxjs,在管道之前
【解决方案2】:

我是 RXJS 的新手,但是这个呢。

如果您将tap 更改为switchMap,则可以删除{dispatch: false}

@Effect({dispatch: false})
public waitForActions(): Observable<any> {
    const waitFor: string[] = [
        SomeAction.EVENT_1,
        SomeAction.EVENT_2,
        SomeAction.EVENT_3,
    ];

    return this._actions$
        .pipe(
            ofType(...waitFor),
            distinct((action: IAction<any>) => action.type),
            bufferCount(waitFor.length),
            tap(console.log),
        );
}

【讨论】:

  • 如何“恢复”这种效果?如果我第一次发出 3 个动作,它可以工作,但是当我第二次发出时,它不会触发。
  • 这可能来自不同的。需要吗? ofType 在定义上已经不同了
  • @Nico without distinct 如果在最后一个waitFor.length 中调度了至少一个waitFor 操作,而不是全部,它将触发。跨度>
  • @bartosz.baczek 查看我的答案,它不止一次起作用。 stackoverflow.com/a/64464568/9092944
【解决方案3】:

另一个带有pipesswitchMapcombineLatest 版本

import { Observable, of } from 'rxjs'
import { combineLatest, switchMap, withLatestFrom } from 'rxjs/operators'

@Effect()
someEffect$: Observable<Actions> = this.actions$.pipe(
  ofType(Action1),
  combineLatest(this.actions$.ofType(Action2)),
  switchMap(() => of({ type: Action3 }))
)

【讨论】:

    【解决方案4】:

    这在 ngrx 8 中对我有用

    waitFor2Actions$ = createEffect(() =>
        combineLatest([
          this.actions$.pipe(ofType(actions.action1)),
          this.actions$.pipe(ofType(actions.action2)),
        ]).pipe(
          switchMap(() => ...),
        )
      );
    

    【讨论】:

      猜你喜欢
      • 2016-12-28
      • 2017-04-20
      • 1970-01-01
      • 2019-02-03
      • 2019-12-23
      • 2016-12-22
      • 1970-01-01
      • 1970-01-01
      • 2019-08-19
      相关资源
      最近更新 更多