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.
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.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
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.