Promise() constructor

The Promise constructor is primarily used to wrap functions that do not already support promises.

Try it

Syntax

new Promise(executor)

Parameters

executor

A function to be executed by the constructor. It receives two functions as parameters: resolutionFunc and rejectionFunc. Any errors thrown in the executor will cause the promise to be rejected, and the return value will be neglected. The semantics of executor are detailed below.

Return value

When called via new, the Promise constructor returns a promise object. The promise object will become resolved when either of the functions resolutionFunc or rejectionFunc are invoked. Note that if you call resolutionFunc or rejectionFunc and pass another Promise object as an argument, it can be said to be "resolved", but still not "settled". See the Promise description for more explanation.

Description

Traditionally (before promises), asynchronous tasks were designed as callbacks.

readFile("./data.txt", (error, result) => {
  // This callback will be called when the task is done, with the
  // final `error` or `result`. Any operation dependent on the
  // result must be defined within this callback.
});
// Code here is immediately executed after the `readFile` request
// is fired. It does not wait for the callback to be called, hence
// making `readFile` "asynchronous".

To take advantage of the readability improvement and language features offered by promises, the Promise() constructor allows one to transform the callback-based API to a promise-based one.

Note: If your task is already promise-based, you likely do not need the Promise() constructor.

The executor is custom code that ties an outcome in a callback to a promise. You, the programmer, write the executor. Its signature is expected to be:

function executor(resolutionFunc, rejectionFunc) {
  // Typically, some asynchronous operation that accepts a callback,
  // like the `readFile` function above
}

resolutionFunc and rejectionFunc are also functions, and you can give them whatever actual names you want. Their signatures are simple: they accept a single parameter of any type.

resolutionFunc(value) // call on resolved
rejectionFunc(reason) // call on rejected

The resolutionFunc value parameter can be another promise object, in which case the promise gets dynamically inserted into the promise chain. The rejectionFunc has semantics close to the throw statement, so reason is typically an Error instance. If either value or reason is omitted, the promise is fulfilled/rejected with undefined.

About the executor, it's important to understand the following:

  • The executor return value is ignored.
  • If an error is thrown in the executor, the promise is rejected.

So the mechanism by which the code within the executor has effect is as follows:

  • At the time when the constructor generates the new Promise object, it also generates a corresponding pair of functions for resolutionFunc and rejectionFunc; these are "tethered" to the Promise object.
  • The code within the executor has the opportunity to perform some operation. The eventual completion of the asynchronous task is communicated with the promise instance via the side effect caused by resolutionFunc or rejectionFunc. The side effect is that the Promise object either becomes "fulfilled", or "rejected".
  • The promise object is stateful: once its state has moved from "pending" to "fulfilled" or "rejected", it stays fulfilled or rejected. Only the first call to resolutionFunc or rejectionFunc affects the promise's state, and subsequent calls to either function can neither change the fulfillment value/rejection reason nor toggle the state from "fulfilled" to "rejected" or opposite.
  • return statements within the executor merely impacts control flow and alters whether a part of the function is executed, but does not have any impact on the promise's fulfillment value.

And so, given all the above, here's a summary of the typical flow:

  1. executor typically wraps some asynchronous operation which provides a callback-based API.
  2. The callback (the one passed to the original callback-based API) is defined within the executor code, so it has access to the resolutionFunc and rejectionFunc.
  3. The promise is informed of the asynchronous task's eventual result, received from the callback, through the invocation of resolutionFunc or rejectionFunc.
  4. Once resolutionFunc or rejectionFunc is called, the promise's state moves from "pending" to either "fulfilled" or "rejected".
  5. The Promise object (asynchronously) invokes any further handlers associated by .then(handleFulfilled) or .catch(handleRejected).
  6. The argument passed to resolutionFunc or rejectionFunc, i.e., the fulfillment value or rejection reason, is passed to the invocation of handleFulfilled and handleRejected as an input parameter (see Chained Promises).

For example, the callback-based readFile API above can be transformed into a promise-based one.

const readFilePromise = (path) => new Promise((resolve, reject) => {
  readFile(path, (error, result) => {
    if (error) {
      reject(error);
    } else {
      resolve(result);
    }
  });
});

readFilePromise("./data.txt")
  .then((result) => console.log(result))
  .catch((error) => console.error("Failed to read data"));

Examples

Creating a new Promise

A Promise object is created using the new keyword and its constructor. This constructor takes a function, called the "executor function", as its parameter. This function should take two functions as parameters. The first of these functions (resolve) is called when the asynchronous task completes successfully and returns the results of the task as a value. The second (reject) is called when the task fails, and returns the reason for failure, which is typically an error object.

const myFirstPromise = new Promise((resolve, reject) => {
  // do something asynchronous which eventually calls either:
  //
  //   resolve(someValue)        // fulfilled
  // or
  //   reject("failure reason")  // rejected
});

Making functions return a Promise

To provide a function with promise functionality, have it return a promise:

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open("GET", url)
    xhr.onload = () => resolve(xhr.responseText)
    xhr.onerror = () => reject(xhr.statusText)
    xhr.send()
  });
}

Specifications

Specification
ECMAScript Language Specification
# sec-promise-constructor

Browser compatibility

BCD tables only load in the browser

See also