【发布时间】: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