Frontend Development 5 min read

Simplify Async Flow with Promise.withResolvers and Promise.try in JavaScript

This article explains how Promise.withResolvers and Promise.try streamline asynchronous control in JavaScript, providing clearer code for handling resolve/reject logic, event aggregation, and unified error handling with practical code examples.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Simplify Async Flow with Promise.withResolvers and Promise.try in JavaScript

Promise.withResolvers

In typical business code a Promise is used as a switch to control flow. The classic pattern looks like this:

<code>let resolve;
const promise = new Promise((res, rej) => {
  resolve = res;
});
promise.then(() => {
  // do some thing
});
// Trigger resolve to run the then‑handler; resolve acts like a switch.
resolve();
</code>

With Promise.withResolvers() the same logic becomes much simpler:

<code>const { promise, resolve } = Promise.withResolvers();
promise.then(() => {
  // do some thing
});
// Trigger resolve to run the then‑handler.
resolve();
</code>

Promise.withResolvers is a static method that returns an object containing the promise , resolve , and reject functions, eliminating the need to create a new Promise manually.

Using it makes flow control more intuitive. For example, you can aggregate a fixed number of events and resolve or reject once the threshold is reached:

<code>function eventHandler(eventsCount) {
  const events = [];
  const { promise, resolve, reject } = Promise.withResolvers();

  return {
    add: (event) => {
      if (events.length < eventsCount) events.push(event);
      if (events.length === eventsCount) resolve(events);
    },
    abort: () => reject("Events aggregation aborted."),
    events: promise,
  };
}

const eventsAggregator = eventHandler(3);

eventsAggregator.events
  .then((events) => console.log("Resolved:", events))
  .catch((reason) => console.error("Rejected:", reason));

eventsAggregator.add("event-one");
eventsAggregator.abort();
eventsAggregator.add("event-two");
eventsAggregator.add("event-three");
// Output: Rejected: Events aggregation aborted.
</code>

In this example the Promise is used to control the number of events; once the count reaches the limit, resolve or reject ends the flow.

The method is supported in Chrome starting from version 119 (released 2023‑10‑31).

Promise.try

Promise.try has existed for a long time in libraries such as Bluebird, Q, and when. Its purpose is to provide a uniform handling mechanism for both synchronous and asynchronous operations.

Consider this example:

<code>const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// Output: next now
</code>

Although f is a synchronous function, placing it in then defers its execution to the end of the current event loop, making it appear asynchronous.

Promise.try lets you execute a function immediately while still returning a promise, so you can chain then and catch uniformly:

<code>const f = () => console.log('now');
Promise.try(f);
console.log('next');
</code>

Because Promise.try provides a consistent API, you can wrap both synchronous and asynchronous calls, enabling unified error handling:

<code>Promise.try(() => database.users.get({ id: userId }))
  .then(...)
  .catch(...);
</code>

In effect, Promise.try simulates a try block for promise chains, similar to how catch handles errors in a promise chain.

JavaScriptPromiseAsync ControlPromise.tryPromise.withResolvers
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login 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.