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.
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/catchPros: 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Cultivation Path
Focused on sharing practical tech content about TypeScript, Vue 3, front-end architecture, and source code analysis.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
