Promise.withResolvers()
Baseline 2024
Newly available
Since March 2024, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.
The Promise.withResolvers()
static method returns an object containing a new Promise
object and two functions to resolve or reject it, corresponding to the two parameters passed to the executor of the Promise()
constructor.
Syntax
Promise.withResolvers()
Parameters
None.
Return value
Description
Promise.withResolvers()
is exactly equivalent to the following code:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
Except that it is more concise and does not require the use of let
.
The key difference when using Promise.withResolvers()
is that the resolution and rejection functions now live in the same scope as the promise itself, instead of being created and used once within the executor. This may enable some more advanced use cases, such as when reusing them for recurring events, particularly with streams and queues. This also generally results in less nesting than wrapping a lot of logic within the executor.
Promise.withResolvers()
is generic and supports subclassing, which means it can be called on subclasses of Promise
, and the result will contain a promise of the subclass type. To do so, the subclass's constructor must implement the same signature as the Promise()
constructor — accepting a single executor
function that can be called with the resolve
and reject
callbacks as parameters.
Examples
Transforming a stream to an async iterable
The use case of Promise.withResolvers()
is when you have a promise that should be resolved or rejected by some event listener that cannot be wrapped inside the promise executor. The following example transforms a Node.js readable stream to an async iterable. Each promise
here represents a single batch of data available, and each time the current batch is read, a new promise is created for the next batch. Note how the event listeners are only attached once, but actually call a different version of the resolve
and reject
functions each time.
async function* readableToAsyncIterable(stream) {
let { promise, resolve, reject } = Promise.withResolvers();
stream.on("error", (error) => reject(error));
stream.on("end", () => resolve());
stream.on("readable", () => resolve());
while (stream.readable) {
await promise;
let chunk;
while ((chunk = stream.read())) {
yield chunk;
}
({ promise, resolve, reject } = Promise.withResolvers());
}
}
Calling withResolvers() on a non-Promise constructor
Promise.withResolvers()
is a generic method. It can be called on any constructor that implements the same signature as the Promise()
constructor. For example, we can call it on a constructor that passes console.log
as the resolve
and reject
functions to executor
:
class NotPromise {
constructor(executor) {
// The "resolve" and "reject" functions behave nothing like the native
// promise's, but Promise.withResolvers() just returns them, as is.
executor(
(value) => console.log("Resolved", value),
(reason) => console.log("Rejected", reason),
);
}
}
const { promise, resolve, reject } = Promise.withResolvers.call(NotPromise);
resolve("hello");
// Logs: Resolved hello
Specifications
Specification |
---|
ES Promise.withResolvers (2023) # sec-promise.withResolvers |
Browser compatibility
BCD tables only load in the browser