JavaScript Create Promise
In general, there are 4 ways to create a new promise in JavaScript:
- Using the
Promise
constructor - Using the static helpers
Promise.resolve()
andPromise.reject()
- Chaining with the
then()
function orcatch()
function - Call an async function
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
Async Functions
When you call an async function, JavaScript returns a new promise. No matter what you return
from an async function, JavaScript always returns a promise, so make sure you await
on
async function calls!
async function test() {
return 42;
}
test() instanceof Promise; // true
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!');