JavaScript Create Promise

Mar 27, 2020

In general, there are 3 ways to create a new promise in JavaScript:

Using the Promise Constructor

The Promise constructor takes a single parameter, an executor function. When you call new Promise(executor), JavaScript immediately executes your executor function with 2 arguments: resolve() and reject().

function executor(resolve, reject) {
  typeof resolve; // 'function'
  typeof reject; // 'function'
}

new Promise(executor);

The executor() function is then responsible for calling resolve() to mark the promise as fulfilled (successful) or rejected (failed).

const success = new Promise(function executor(resolve) {
  resolve('OK');
});

const fail = new Promise(function executor(resolve, reject) {
  reject(new Error('Oops'));
});

const str = await success;
str; // 'OK'

const err = await fail.catch(err => err);
err.message; // Oops

Using Static Helpers

The Promise.resolve() function lets you create a new promise that is immediately fulfilled.

const p = Promise.resolve(42);
p.then(v => {
  v; // 42
});

You can think of Promise.resolve(v) as short for new Promise(resolve => resolve(v)).

Similarly, the Promise.reject() function lets you create a new promise that is immediately rejected.

const p = Promise.reject(new Error('Oops!'));
p.catch(err => {
  err.message; // 'Oops!'
});

Be careful with Promise.reject(): if you don't immediately add a .catch() handler to your new promise, you'll get an unhandled promise rejection.

then() and catch()

When you call .then() or .catch(), JavaScript creates a new promise.

const p = Promise.resolve('Hello');

const p2 = p.then(str => `${str} World`);

p2 instanceof Promise; // true
p2 === p; // false

Without Executing

JavaScript promises are "hot" in the sense that JavaScript executes the executor function immediately.

If you find yourself wanting a "cold" promise in the sense that your promise doesn't execute until you await on it, you should just use an async function. Calling an async function returns a new promise every time.

async function getAnswer() {
  return 42;
}

const p1 = getAnswer();
p1 instanceof Promise; // true

const p2 = getAnswer();
p2 instanceof Promise; // true
p2 === p1; // false

Another common alternative is the deferred pattern, where you create a promise that has resolve() and reject() functions that you can call outside the executor() function.

Promise.deferred = function() {
  let resolve = null;
  let reject = null;
  const p = new Promise((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;
  });
  return Object.assign(p, { resolve, reject });
};

const p = Promise.deferred();

p.then(v => {
  v; // 42
});

p.resolve(42);

However, the deferred pattern is considered an antipattern. That's because synchronous errors that occur outside the executor function won't reject the promise!

// JavaScript catches any errors that occur in the promise executor
// and treats them as a promise rejection.
const p1 = new Promise(() => { throw new Error('Oops!'); });
p1.catch(err => {
  err.message; // 'Oops!'
});

// With `deferred`, you're responsible for handling errors that
// occur outside the executor. If you forget, your promise will
// be pending forever like `p2` below.
const p2 = Promise.deferred();
throw new Error('Oops!');

More Fundamentals Tutorials