Cancellable Task Runner With Cleanup

A cancellable task runner that stops starting new work on abort and always runs registered cleanup, even when a task throws mid-flight.

JavascriptResilience

Code

export class AbortError extends Error {
  constructor(message = "run aborted") {
    super(message);
    this.name = "AbortError";
  }
}

export async function runTasks(tasks, options = {}) {
  const { signal, onCleanup } = options;
  const results = [];
  const throwIfAborted = () => {
    if (signal?.aborted) throw new AbortError();
  };

  throwIfAborted();
  for (const task of tasks) {
    let cleanupRan = false;
    const cleanup = async () => {
      if (cleanupRan) return;
      cleanupRan = true;
      if (onCleanup) await onCleanup();
    };
    try {
      const value = await task({ signal });
      results.push({ ok: true, value });
      await cleanup();
    } catch (err) {
      await cleanup();
      throw err;
    }
    throwIfAborted();
  }
  return results;
}