应检查父任务活跃状态而非仅判空,推荐用isrunning标识;需同步校验取消信号;fork与注册须原子化;typescript下可用activetask类型约束。
在使用 fork() 启动子任务前,若父任务引用(如 redux store 中的 task 对象、saga 中的 task descriptor、或自定义任务管理器中的 parenttask 句柄)已提前被 cancel / abort / cleanup,直接 fork 可能导致子任务失去上下文、无法被正确追踪、甚至引发空指针或竞态异常。关键不是“加锁”,而是建立轻量、可验证、不可绕过的守卫逻辑。
守卫一:检查父任务的活跃状态标识
父任务应自带明确的生命周期标记(如 isRunning: boolean、status: 'pending' | 'running' | 'done'),而非依赖引用是否存在。仅判空不够——引用可能还在,但语义上已失效。
- ✅ 推荐写法:
if (!parentTask || !parentTask.isRunning) return; - ❌ 避免写法:
if (!parentTask) return;(引用未 null,但已被 cancel) - 若用 redux-saga,可用
task.isRunning属性(v1.2+ 支持),它由 runtime 维护,比手动 flag 更可靠
守卫二:绑定取消信号并同步校验
fork 前主动读取一次父任务关联的取消信号(如 AbortSignal、cancellation promise、或 saga 的 task.done),确认尚未被触发。
- 若用 AbortController:
if (parentAbortSignal.aborted) return; - 若用 Promise-based cancellation:
if (parentCancelPromise settled as 'cancelled') return; - 在 saga 中,可结合
race预检:const result = yield race({ check: delay(0), cancelled: parentTask.done }); if (result.cancelled) return;
守卫三:原子化 fork + 注册,避免中间态裸露
不要先 fork 再手动注册子任务到父任务列表;应将“创建 + 关联”封装为一个不可分割的操作,由任务管理器统一保障。
- 例如自定义 taskManager.fork(childFn, options),内部自动:检查 parent 状态 → 创建子 task → 将子 task 加入 parent.children → 返回子 task
- 这样即使父任务在 fork 过程中被 cancel,管理器可在注册前拦截(如抛出 CancellationError 并拒绝 fork)
- 避免手写:
const child = fork(...); parent.addChild(child);—— 两步之间存在时间窗口
守卫四:静态类型 + 编译期约束(TypeScript 场景)
利用类型系统让“已断开的父任务”无法进入 fork 流程。
- 定义
ActiveTask类型,仅包含isRunning: true的 task 对象 - 让
forkChild(parent: ActiveTask, ...)的参数类型强制要求父任务处于活跃态 - 配合运行时守卫(如 assertActive(parent)),实现类型安全 + 行为安全双保险
不复杂但容易忽略:守卫不是越多越好,关键是选对那个最接近语义本质的判断点——通常是父任务自身的状态标识,而不是间接推导(比如查 store 是否有值、看某个副作用是否执行过)。把守卫内聚在 fork 调用点附近,不分散、不延迟、不复用过期快照。










