RxJS Observable Effect问题:如何先分发位置Action再分发日出日落Action?
解决NgRx Effect中无法依次分发多个Action的问题
我完全懂你的困扰——你希望这个Effect先触发PositionUpdateAction或PositionFailedAction,再触发SunriseSunsetAction,但现在只有后者被分发出来。问题其实出在你原代码的map操作逻辑上:它把前面生成的位置相关Action直接“替换”成了SunriseSunsetAction,导致前两个Action根本没机会被发送出去。
核心问题拆解
原代码里,在fromPromise(this.geo.getCurrentPosition())的pipe链中,你先用map生成了PositionUpdateAction,但紧接着又用另一个map把这个结果转换成了SunriseSunsetAction。这相当于你直接丢弃了第一个Action,只保留了最后一次转换的结果,所以自然只有SunriseSunsetAction被分发。
正确实现方案
要触发多个Action,你需要在成功/失败的分支里返回一个包含多个Action的Observable,并用concat来严格保证它们的执行顺序(先位置Action,再日出日落Action)。修改后的代码如下:
@Effect() position$: Observable<Action> = this.actions$.pipe( ofType(ActionType.GetPosition), mergeMap(() => fromPromise(this.geo.getCurrentPosition()).pipe( map(value => { // 成功时,按顺序返回两个Action的Observable return concat( of(new UiActions.PositionUpdateAction(value)), of(new UiActions.SunriseSunsetAction( this.sun.calculateSunriseSunsetWindows(value.lat, value.long) )) ); }), catchError((err) => { // 失败时,先返回失败Action,再返回日出日落Action(可根据业务需求调整是否保留后者) return concat( of(new UiActions.PositionFailedAction(err)), of(new UiActions.SunriseSunsetAction(/* 这里可传入默认值或空值,根据逻辑调整 */)) ); }), // 把嵌套的Observable展开成单个Action流,让Effect能正确分发每个Action mergeAll() ) ) );
关键调整说明
- 用
concat组合Action流:concat会按顺序订阅并发送每个Observable的结果,确保先触发位置相关Action,再触发日出日落Action,完全符合你的需求。 - 展开嵌套Observable:因为我们在
map里返回的是一个由concat生成的Observable,所以需要用mergeAll()把嵌套的流展开成顶层的Action流,这样Effect才能正确识别并分发每个Action。 - 失败分支的灵活处理:如果失败时不需要触发
SunriseSunsetAction,直接返回of(new UiActions.PositionFailedAction(err))即可,不需要concat后续Action。
另一种简洁写法(可选)
你也可以用tap先手动分发位置Action,再返回日出日落Action,但这种方式需要注入Store来调用dispatch,不如第一种方式符合NgRx Effect的设计理念(推荐通过返回Observable来分发Action)。示例如下:
@Effect() position$: Observable<Action> = this.actions$.pipe( ofType(ActionType.GetPosition), switchMap(() => fromPromise(this.geo.getCurrentPosition()).pipe( tap(value => this.store.dispatch(new UiActions.PositionUpdateAction(value))), map(value => new UiActions.SunriseSunsetAction( this.sun.calculateSunriseSunsetWindows(value.lat, value.long) )), catchError((err) => { this.store.dispatch(new UiActions.PositionFailedAction(err)); // 失败时若不需要后续Action,可返回EMPTY return of(new UiActions.SunriseSunsetAction(/* 默认值 */)); // return EMPTY; }) ) ) );
还是第一种返回多个Action的方式更易测试和维护,建议优先采用。
内容的提问来源于stack exchange,提问作者brent
相关产品推荐
相关产品推荐

