Skip to content
📚 3 min read

Sequential Task Execution Examples ​

Learn how to execute tasks in sequence with proper error handling and state management.

Basic Usage ​

typescript
// Simple sequential execution
async function executeSequential<T>(tasks: (() => Promise<T>)[]): Promise<T[]> {
  const results: T[] = [];
  for (const task of tasks) {
    results.push(await task());
  }
  return results;
}

// Sequential with intermediate results
async function executeWithIntermediateResults<T>(
  tasks: (() => Promise<T>)[],
  onResult: (result: T, index: number) => void
): Promise<T[]> {
  const results: T[] = [];
  for (let i = 0; i < tasks.length; i++) {
    const result = await tasks[i]();
    results.push(result);
    onResult(result, i);
  }
  return results;
}

Advanced Patterns ​

Pipeline Processing ​

typescript
type PipelineStage<T, R> = (input: T) => Promise<R>;

class Pipeline<T> {
  private stages: PipelineStage<any, any>[] = [];

  addStage<R>(stage: PipelineStage<T, R>): Pipeline<R> {
    this.stages.push(stage);
    return this as unknown as Pipeline<R>;
  }

  async execute(input: T): Promise<any> {
    let current = input;
    for (const stage of this.stages) {
      current = await stage(current);
    }
    return current;
  }
}

// Usage example
const dataPipeline = new Pipeline<string>()
  .addStage(async (text) => text.toUpperCase())
  .addStage(async (text) => text.split(''))
  .addStage(async (chars) => chars.reverse())
  .addStage(async (chars) => chars.join(''));

State Machine Execution ​

typescript
interface State {
  name: string;
  execute: () => Promise<string>;
}

class StateMachine {
  private states: Map<string, State> = new Map();
  private transitions: Map<string, Set<string>> = new Map();

  addState(state: State): this {
    this.states.set(state.name, state);
    return this;
  }

  addTransition(from: string, to: string): this {
    if (!this.transitions.has(from)) {
      this.transitions.set(from, new Set());
    }
    this.transitions.get(from)!.add(to);
    return this;
  }

  async execute(initialState: string): Promise<string[]> {
    const history: string[] = [];
    let currentState = initialState;

    while (true) {
      const state = this.states.get(currentState);
      if (!state) throw new Error(`Invalid state: ${currentState}`);

      history.push(currentState);
      const nextState = await state.execute();

      if (!nextState) break;

      const allowedTransitions = this.transitions.get(currentState);
      if (!allowedTransitions?.has(nextState)) {
        throw new Error(`Invalid transition: ${currentState} -> ${nextState}`);
      }

      currentState = nextState;
    }

    return history;
  }
}

Dependency-Based Execution ​

typescript
interface Task<T> {
  id: string;
  execute: () => Promise<T>;
  dependencies: string[];
}

class DependencyExecutor<T> {
  private tasks = new Map<string, Task<T>>();
  private results = new Map<string, T>();
  private completed = new Set<string>();

  addTask(task: Task<T>): this {
    this.tasks.set(task.id, task);
    return this;
  }

  private async executeTask(taskId: string): Promise<T> {
    if (this.completed.has(taskId)) {
      return this.results.get(taskId)!;
    }

    const task = this.tasks.get(taskId);
    if (!task) throw new Error(`Task not found: ${taskId}`);

    // Execute dependencies first
    await Promise.all(
      task.dependencies.map((depId) => this.executeTask(depId))
    );

    const result = await task.execute();
    this.results.set(taskId, result);
    this.completed.add(taskId);

    return result;
  }

  async execute(): Promise<Map<string, T>> {
    for (const taskId of this.tasks.keys()) {
      await this.executeTask(taskId);
    }
    return this.results;
  }
}

// Usage example
const executor = new DependencyExecutor<string>()
  .addTask({
    id: 'fetch',
    dependencies: [],
    execute: async () => 'data',
  })
  .addTask({
    id: 'process',
    dependencies: ['fetch'],
    execute: async () => 'processed',
  })
  .addTask({
    id: 'save',
    dependencies: ['process'],
    execute: async () => 'saved',
  });

Retry with Backoff ​

typescript
interface RetryOptions {
  maxAttempts: number;
  initialDelay: number;
  maxDelay: number;
  backoffFactor: number;
}

async function executeWithRetry<T>(
  task: () => Promise<T>,
  options: RetryOptions
): Promise<T> {
  let delay = options.initialDelay;

  for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
    try {
      return await task();
    } catch (error) {
      if (attempt === options.maxAttempts) throw error;

      console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms:`, error);

      await new Promise((resolve) => setTimeout(resolve, delay));

      delay = Math.min(delay * options.backoffFactor, options.maxDelay);
    }
  }

  throw new Error('Should not reach here');
}