You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 03:25:23