Angular NgRx:多次分发Action后,如何在全部完成时触发成功Action
解决方案
针对你遇到的NgRx批量任务生成后触发完成Action的问题,推荐两种可行方案,优先选择第一种(逻辑更简洁可控):
方案一:改用批量触发Action(推荐)
核心思路是将循环分发单个generateTaskCycle Action的逻辑,改为分发一个携带所有任务 bundle 的批量Action,让Effect全程掌控处理流程,在所有任务处理完成后自动触发完成Action。
1. 新增批量Action定义
// 在你的Actions文件中新增: export const generateTaskCycles = createAction( '[Tasks Generator] Generate Task Cycles', props<{ taskGenerationBundles: TaskGenerationBundle[] }>() ); export const generateTasksComplete = createAction( '[Tasks Generator] Generate Tasks Complete' );
2. 修改Effect处理逻辑
generateTaskCycles$ = createEffect(() => this.actions$.pipe( ofType(fromAppPlanActions.TasksGeneratorActions.generateTaskCycles), map(action => action.taskGenerationBundles), // 用concatMap保证每个bundle顺序处理,依赖最新Store状态 concatMap(bundles => from(bundles).pipe( concatMap(bundle => of(bundle).pipe( concatLatestFrom(() => [ this.store.select(fromAppPlanSelectors.taskSelectors.selectAllTasks), this.store.select(fromAppPlanSelectors.taskDependencySelectors.selectAllTaskDependencies), this.store.select(fromAppPlanSelectors.planSelectors.selectCurrentPlan), ]), map(([taskGenerationBundle, allTasks, allTaskDependencies, currentPlan]) => { if (currentPlan !== null) { // 原有的任务生成与更新逻辑保持不变 const newTasksToAdd = this.taskGenerationService.createTasks( taskGenerationBundle.cycleId, taskGenerationBundle.fuelData, taskGenerationBundle.orderIdArray, taskGenerationBundle.parentTask, taskGenerationBundle.startDate, ); const newTaskDependenciesToAdd: TaskDependency[] = []; const updatedTasksAndTaskDependencies = this.taskService.addNewTasksAndTaskDependencies( newTasksToAdd, newTaskDependenciesToAdd, allTasks, allTaskDependencies, currentPlan ); this.taskGenerationService.allNewTasksToAdd.push(...newTasksToAdd); this.taskGenerationService.allNewTaskDependenciesToAdd.push(...newTaskDependenciesToAdd); return fromAppPlanActions.TasksGeneratorActions.updateTasksAndTaskDependenciesInStore( { tasksAndTaskDependenciesToUpdate: updatedTasksAndTaskDependencies } ); } else { return fromAppPlanActions.TasksGeneratorActions.generateTasksFail( { errorMessage: 'Warning - Failed to generate Tasks. Could not get the current Plan.' } ); } }) )), // 所有bundle处理完成后,触发完成Action endWith(fromAppPlanActions.TasksGeneratorActions.generateTasksComplete()) )) ) );
3. 修改TaskGenerationService触发逻辑
替换原来的forEach循环分发单个Action的代码,改为分发批量Action:
// 假设userOptionArray是用户选择的选项数组 const taskGenerationBundles = userOptionArray.map(option => { // 这里根据每个option构建对应的taskGenerationBundle对象 return { cycleId: /* 对应值 */, fuelData: /* 对应值 */, orderIdArray: /* 对应值 */, parentTask: /* 对应值 */, startDate: /* 对应值 */, }; }); this.store.dispatch( fromAppPlanActions.TasksGeneratorActions.generateTaskCycles({ taskGenerationBundles }) );
方案二:保留单个Action分发,跟踪处理进度
如果无法修改批量触发逻辑,可以给每个generateTaskCycle Action添加批次信息,通过额外的Effect跟踪所有任务的处理完成状态。
1. 修改单个Action定义
export const generateTaskCycle = createAction( '[Tasks Generator] Generate Task Cycle', props<{ taskGenerationBundle: TaskGenerationBundle; batchTotal: number; // 总任务数 batchIndex: number; // 当前任务索引 }>() ); export const taskCycleProcessed = createAction( '[Tasks Generator] Task Cycle Processed', props<{ batchTotal: number; batchIndex: number }>() ); export const generateTasksComplete = createAction( '[Tasks Generator] Generate Tasks Complete' );
2. 修改原Effect逻辑
在每个任务处理完成后,分发一个标记单个任务处理完成的Action:
generateTaskCycle$ = createEffect(() => this.actions$.pipe( ofType(fromAppPlanActions.TasksGeneratorActions.generateTaskCycle), map(action => ({ bundle: action.taskGenerationBundle, batchTotal: action.batchTotal, batchIndex: action.batchIndex })), concatMap(({ bundle, batchTotal, batchIndex }) => of(bundle).pipe( concatLatestFrom(() => [ this.store.select(fromAppPlanSelectors.taskSelectors.selectAllTasks), this.store.select(fromAppPlanSelectors.taskDependencySelectors.selectAllTaskDependencies), this.store.select(fromAppPlanSelectors.planSelectors.selectCurrentPlan), ]), map(([taskGenerationBundle, allTasks, allTaskDependencies, currentPlan]) => { if (currentPlan !== null) { // 原任务生成逻辑不变 const newTasksToAdd = this.taskGenerationService.createTasks(/* 参数 */); // ...省略其他逻辑 return fromAppPlanActions.TasksGeneratorActions.updateTasksAndTaskDependenciesInStore(/* 参数 */); } else { return fromAppPlanActions.TasksGeneratorActions.generateTasksFail(/* 参数 */); } }), // 无论成功失败,都标记当前任务处理完成 map(() => fromAppPlanActions.TasksGeneratorActions.taskCycleProcessed({ batchTotal, batchIndex })) )) ) );
3. 新增跟踪完成状态的Effect
generateTasksComplete$ = createEffect(() => this.actions$.pipe( ofType(fromAppPlanActions.TasksGeneratorActions.taskCycleProcessed), // 用scan累计每个批次的处理进度 scan((acc, action) => { const { batchTotal, batchIndex } = action; if (!acc[batchTotal]) { acc[batchTotal] = new Set<number>(); } acc[batchTotal].add(batchIndex); // 检查当前批次是否全部处理完成 if (acc[batchTotal].size === batchTotal) { acc.completedBatches.add(batchTotal); delete acc[batchTotal]; // 清理已完成的批次跟踪 } return acc; }, { completedBatches: new Set<number>() } as { [key: number]: Set<number>; completedBatches: Set<number> }), // 只有当有新完成的批次时才触发后续逻辑 filter(acc => acc.completedBatches.size > 0), // 清理完成标记,避免重复触发 tap(acc => acc.completedBatches.clear()), // 分发完成Action map(() => fromAppPlanActions.TasksGeneratorActions.generateTasksComplete()) ) );
4. 修改TaskGenerationService触发逻辑
分发单个Action时带上批次信息:
const total = userOptionArray.length; userOptionArray.forEach((option, index) => { const bundle = /* 构建taskGenerationBundle */; this.store.dispatch( fromAppPlanActions.TasksGeneratorActions.generateTaskCycle({ taskGenerationBundle: bundle, batchTotal: total, batchIndex: index }) ); });
内容的提问来源于stack exchange,提问作者TDC
相关产品推荐
相关产品推荐

