---
title: "Promise.try in JavaScript: One Error Path for Sync and Async"
description: "Stop mixing try/catch with Promise chains - JavaScript's new Promise.try handles return values, Promises, and errors uniformly"
canonical: "https://www.trevorlasn.com/blog/promise-try-in-javascript"
author: "Trevor I. Lasn"
published: "2024-11-10"
last-updated: "2026-02-15"
topics: ["JavaScript"]
---

# Promise.try in JavaScript: One Error Path for Sync and Async

Stop mixing try/catch with Promise chains - JavaScript's new Promise.try handles return values, Promises, and errors uniformly

By Trevor I. Lasn · Published 2024-11-10 · Updated 2026-02-15 · https://www.trevorlasn.com/blog/promise-try-in-javascript

JavaScript now has a Promise utility that makes handling potentially async functions cleaner and safer. [Promise.try](https://tc39.es/proposal-promise-try/) lets you wrap any function in a Promise, whether it's async or not, while maintaining optimal execution timing.

[Browser support for `promise-try` on webstatus.dev](https://webstatus.dev/features/promise-try)
<div
  class="flex items-center text-sm pl-4 pr-2 mb-5 mt-5 text-indigo-700 rounded-lg bg-surface dark:text-indigo-300"
  role="note"
>
  <svg 
    class="flex-shrink-0 inline w-4 h-4 me-3" 
    aria-hidden="true"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
    stroke-width="2"
  >
    <path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
  </svg>
  <div>
    <a href="https://github.com/tc39/proposals/blob/main/finished-proposals.md"
        target="_blank"
        rel="noopener"
        class="font-medium text-md mb-1 text-indigo-700 dark:text-indigo-300"
      >
      `Promise.try` TC39 Finished Proposals
    </a>
  </div>
</div>

### The Core Problem

Handling functions that might be sync or async requires mixing different error handling patterns:

```js title="JavaScript"
function getUserData(id) {
  // We need try/catch for sync errors
  try {
    validateId(id);
    
    // Might return cached data synchronously
    if (id in cache) {
      return cache[id];
    }
    
    // Might return a Promise
    return fetch(`/api/users/${id}`);
  } catch (syncError) {
    handleSyncError(syncError);
  }
}
```

The problem? You never know which behavior you'll get. Currently, there are two common ways to handle this, but neither is ideal:

```js title="JavaScript"
// Method 1: Using Promise.resolve().then()
Promise.resolve().then(() => getUserData(123));
// ❌ Forces everything to be async
// ❌ Even cached data waits for the next tick
// ✓ But at least it catches errors

// Method 2: Using new Promise
new Promise(resolve => resolve(getUserData(123)));
// ❌ Verbose and clunky
// ❌ Easy to get wrong
// ✓ Runs sync code immediately
```

We need a better way to:

- Run synchronous code immediately (for better performance)
- Handle asynchronous code when needed
- Catch any errors that might occur
- Do all this with clean, readable syntax

### The Solution: `Promise.try`

`Promise.try` gives us one clean way to handle all cases:

```js title="JavaScript"
// Clean, safe, and optimal timing
Promise.try(() => getUserData(123))
  .then(user => {
    // Gets called with:
    // - Immediate values (from cache)
    // - Resolved Promise values (from fetch)
  })
  .catch(error => {
    // Catches both:
    // - Synchronous throws
    // - Promise rejections
  });
```
<br />

### Why It's Better
`Promise.try` has three key advantages:

```js title="JavaScript"
// 1. Runs synchronously when possible
Promise.try(() => "instant") // Executes immediately
  .then(x => console.log(x));

// 2. Catches all errors reliably
Promise.try(() => {
  throw new Error('boom');
})
  .catch(err => console.log('Caught:', err));

// 3. Handles both sync and async naturally
Promise.try(() => {
  if (Math.random() > 0.5) {
    return "sync value";
  }
  return fetch('/api/data');
});
```

`Promise.try` runs synchronous code immediately for better performance, while still handling async operations when needed. It catches every type of error through a single catch handler, and does it all with clean, readable syntax. Think of it as the Promise equivalent of a try-catch block - a safe way to execute code that might fail or might be async, without having to know which it is ahead of time.

This is particularly powerful when working with functions that might change their behavior over time. Your error handling remains consistent whether the function returns immediately or needs to make an API call. No more wrapping everything in try-catch blocks and then also handling Promise rejections separately.

### References

- [https://tc39.es/ecma262/#sec-promise.try](https://tc39.es/ecma262/#sec-promise.try)
- [https://tc39.es/proposal-promise-try/](https://tc39.es/proposal-promise-try/)
- [https://github.com/tc39/proposal-promise-try](https://github.com/tc39/proposal-promise-try)
