AsyncStateMachine.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. export type StateHandler<TState extends string> = () => Promise<TState | null>;
  2. export interface StateTransition<TState extends string> {
  3. from: TState | null;
  4. to: TState;
  5. }
  6. /**
  7. * 顺序异步状态机。
  8. *
  9. * 每个状态完成后返回下一个状态,返回 null 表示流程结束。
  10. * 状态处理器抛出的异常由调用方决定如何进入失败状态。
  11. */
  12. export class AsyncStateMachine<TState extends string> {
  13. private readonly handlers = new Map<TState, StateHandler<TState>>();
  14. private currentState: TState | null = null;
  15. private running = false;
  16. constructor(
  17. private readonly onTransition?: (transition: StateTransition<TState>) => void,
  18. ) { }
  19. get current(): TState | null {
  20. return this.currentState;
  21. }
  22. register(state: TState, handler: StateHandler<TState>): this {
  23. if (this.handlers.has(state)) {
  24. throw new Error(`State already registered: ${state}`);
  25. }
  26. this.handlers.set(state, handler);
  27. return this;
  28. }
  29. async start(initialState: TState): Promise<void> {
  30. if (this.running) {
  31. throw new Error('State machine is already running');
  32. }
  33. this.running = true;
  34. try {
  35. let nextState: TState | null = initialState;
  36. while (nextState !== null) {
  37. nextState = await this.execute(nextState);
  38. }
  39. } finally {
  40. this.running = false;
  41. }
  42. }
  43. async transition(state: TState): Promise<TState | null> {
  44. if (this.running) {
  45. throw new Error('Cannot transition manually while state machine is running');
  46. }
  47. return this.execute(state);
  48. }
  49. private async execute(state: TState): Promise<TState | null> {
  50. const handler = this.handlers.get(state);
  51. if (!handler) {
  52. throw new Error(`State is not registered: ${state}`);
  53. }
  54. const previous = this.currentState;
  55. this.currentState = state;
  56. this.onTransition?.({ from: previous, to: state });
  57. return handler();
  58. }
  59. }