Goodbye try/catch: Better Alternatives for JavaScript Async Error Handling

The article examines why traditional try/catch becomes cumbersome in asynchronous JavaScript, then presents three cleaner solutions—a Stage‑1 try‑operator proposal, a custom safeAwait helper, and the community library await‑to‑js—detailing their syntax, benefits, drawbacks, and suitable use cases.

Full-Stack Cultivation Path
Full-Stack Cultivation Path
Full-Stack Cultivation Path
Goodbye try/catch: Better Alternatives for JavaScript Async Error Handling

When many asynchronous calls are wrapped in try/catch, the code becomes verbose, nested, and hard to read. Example:

try {
  const data = await fetchUser();
  doSomething(data);
} catch (e) {
  console.error('error', e);
}

Repeating this pattern for dozens of calls leads to duplicated error handling and loss of linear flow.

Language‑level try operator proposal

Proposal address: https://github.com/arthurfiorette/proposal-try-operator

The proposal introduces a try expression that returns a three‑element tuple [ok, err, result] instead of throwing.

const [ok, err, result] = try await fetchUser();

If the promise resolves, ok is true and result holds the value.

If the promise rejects, ok is false and err contains the error object.

This mirrors Go’s val, err := fn() and Rust’s Result, moving error handling from control flow to value decomposition. The proposal is at Stage 1 and not yet part of JavaScript.

Custom wrapper: safeAwait

export type SafeAwaitResult<T> =
  | [true, null, T]
  | [false, Error, null];

export async function safeAwait<T>(promise: Promise<T>): Promise<SafeAwaitResult<T>> {
  try {
    const result = await promise;
    return [true, null, result];
  } catch (err: any) {
    const error = err instanceof Error ? err : new Error(String(err));
    return [false, error, null];
  }
}

Usage example:

const [ok, err, user] = await safeAwait(fetchUser());
if (!ok) {
  console.error('request failed:', err);
  return;
}
console.log('user data:', user);

Chaining multiple calls without nesting:

const [ok1, err1, user] = await safeAwait(fetchUser());
if (!ok1) return handle(err1);
const [ok2, err2, posts] = await safeAwait(fetchPosts(user.id));
if (!ok2) return handle(err2);
renderDashboard(user, posts);

Benefits observed in the code:

Clear semantics: ok indicates success, err holds the error, data holds the result.

No explicit try/catch in calling code, resulting in linear, readable flow.

Strong TypeScript typing enables IDE assistance and generic inference.

Reusable across a project, providing a unified error‑handling style.

Third‑party library: await-to-js

Installation: npm install await-to-js Usage example:

import to from 'await-to-js';
const [err, data] = await to(fetchUser());
if (err) return handle(err);
render(data);

The library offers a ready‑to‑use implementation that converts a promise into an [error, result] tuple, adding only a single dependency.

Comparison of the three approaches

try/catch

Pros: native support, clear semantics.

Cons: verbose, nested, difficult to compose.

Best for: complex branching logic where native control flow is required. safeAwait (custom)

Pros: concise, type‑safe, composable.

Cons: requires maintaining the wrapper code.

Best for: medium to large projects that want a unified style. await-to-js (library)

Pros: plug‑and‑play, mature community.

Cons: introduces an additional dependency.

Best for: quick adoption and team collaboration.

Evolution of error handling

Moving from repetitive try/catch toward value‑based error handling—whether via the upcoming language proposal, a custom safeAwait helper, or the await-to-js library—makes asynchronous error handling clearer, more elegant, and better aligned with functional programming patterns.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScripterror handlingasynctry/catchawait-to-jssafeAwait
Full-Stack Cultivation Path
Written by

Full-Stack Cultivation Path

Focused on sharing practical tech content about TypeScript, Vue 3, front-end architecture, and source code analysis.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.