FIELD NOTE 011TypeScriptReviewed July 2026

Async/Await Error Handler

Simplifies async/await error handling without try/catch blocks, returns [error, result] tuple

Copy the complete example or adapt its typed interface to your project. Comments are kept inside the snippet so the intent travels with the code.

pattern.ts
/**
 * Async/await error handler
 * @param promise Promise to execute
 * @returns Tuple [error, result]
 */
async function asyncHandler<T>(promise: Promise<T>): Promise<[Error | null, T | null]> {
  try {
    const result = await promise;
    return [null, result];
  } catch (error) {
    return [error as Error, null];
  }
}

// Usage Example
const fetchData = () => fetch('https://api.example.com/data').then(res => res.json());

// Without try/catch
const [error, data] = await asyncHandler(fetchData());
if (error) {
  console.error('Fetch failed:', error);
} else {
  console.log('Data:', data);
}

Reduces try/catch boilerplate in async functions, makes error handling more declarative and readable

Implementation note

Review the input assumptions and browser or runtime support before adopting a utility unchanged. Small helpers are easiest to maintain when their contract stays narrow.

How it works

The wrapper awaits a promise and converts its two possible outcomes into a tuple. Callers can destructure the error and result in one expression, avoiding a local try/catch when both outcomes are ordinary control flow.

What to watch for

A tuple can be misread because both positions may be undefined, and broad unknown errors still need narrowing. Converting rejection into data also means unhandled failures no longer surface automatically through normal promise diagnostics.

Production use

Use the pattern consistently at integration boundaries where failure is expected and immediately handled. A named Result object is clearer for larger APIs, while exceptions remain appropriate when a caller cannot recover locally.

Before you ship

  • Confirm the input contract with tests that cover empty, invalid, boundary, and representative real-world values before sharing the helper across a codebase.
  • Keep the function focused on one responsibility and name any project-specific policy explicitly instead of hiding it inside a generic-looking utility.
  • Measure behavior in the browsers or runtime versions your product supports, especially when timing, storage, DOM state, locale, or network access is involved.
  • Document the return type, mutation behavior, failure mode, and performance characteristics beside the call site so future maintainers can evaluate changes safely.
  • Compare the example with current official platform documentation when it relies on a browser API, language feature, or runtime behavior that may change over time.
  • If the helper crosses a trust boundary, validate again on the server and avoid treating browser-side checks, TypeScript annotations, or formatted output as a security guarantee.
  • Add a small regression test whenever a real edge case is discovered; concrete examples preserve the reason behind a guard more reliably than a general comment.