如何在NgRx Effect中合并多请求响应并仅触发一次成功动作?
解决NgRx Effect合并多请求结果的方案
你的问题核心是当前代码用merge会在每个请求完成时单独触发getCustomerSuccess,要实现统一获取全部结果后触发一次动作,可以按以下方式修改:
步骤1:定义汇总结果的Action
首先需要新增一个用于承载所有请求结果的Action(如果还没有的话):
// 假设你的Customer类型已经定义 export const refreshSuccess = createAction( '[Customer] Refresh All Success', props<{ successCustomers: Customer[]; failedRequests: { id: string; errorMsg: string }[] }>() );
步骤2:修改Effect逻辑
将原有的merge替换为forkJoin,并调整每个请求的处理逻辑,先收集所有结果再统一触发动作:
refresh$ = createEffect(() => this.actions$.pipe( ofType(CustomerActions.refresh), delay(1000), withLatestFrom(this.facade.customerIds$), // 注意原代码首字母大写的WithLatestFrom要改成小写withLatestFrom exhaustMap(([_, customerIds]) => { // 为每个id创建一个Observable,返回成功/失败的结果对象 const customerObservables = customerIds.map(id => this.customersService.getCustomer(id).pipe( map(customer => ({ type: 'success', data: customer })), catchError(err => of({ type: 'failed', id, errorMsg: err.message })) ) ); // 用forkJoin等待所有请求完成,拿到全部结果 return forkJoin(customerObservables).pipe( map(results => { // 分离成功和失败的结果 const successCustomers = results .filter(r => r.type === 'success') .map(r => r.data as Customer); const failedRequests = results .filter(r => r.type === 'failed') .map(r => ({ id: r.id, errorMsg: r.errorMsg })); // 触发汇总结果的Action return CustomerActions.refreshSuccess({ successCustomers, failedRequests }); }) ); }) ) );
关键修改点说明
- 替换
merge为forkJoin:merge会在每个请求完成时立即发射结果,而forkJoin会等待所有Observable都完成后,一次性返回所有结果的数组,这是实现统一结果的核心。 - 每个请求单独处理错误:必须在每个
getCustomer的Observable中添加catchError,否则只要有一个请求失败,forkJoin会直接抛出错误,导致无法获取其他成功的结果。 - 先收集结果再触发动作:不再在单个请求完成时dispatch动作,而是等所有请求结束后,整理结果再触发汇总Action。
可选:保留单个失败动作
如果需要同时保留每个失败请求的单独动作(比如单独更新状态),可以在forkJoin的map里额外dispatch这些动作:
return forkJoin(customerObservables).pipe( tap(results => { // 单独触发失败动作 results.forEach(r => { if (r.type === 'failed') { this.store.dispatch(CustomerActions.getCustomerFailed(r.id, r.errorMsg)); } }); }), map(results => { // 处理成功结果并触发汇总动作 const successCustomers = results .filter(r => r.type === 'success') .map(r => r.data as Customer); return CustomerActions.refreshSuccess({ successCustomers }); }) );
内容的提问来源于stack exchange,提问作者Sarah Mandana
相关产品推荐
相关产品推荐

