JavaScript Promise states and rules

Promises states

A promise can be in 1 of 3 states:

  • Fulfilled: The resolve function was called.
  • Rejected: The reject function was called.
  • Pending: The resolve or reject functions were not called yet.
  • Settled: The promise is settled if it's not pending. Settled is not a valid state of the promise, but it's a term used frequently.

Promises rules

  • If the resolve function is called, the function reject will not be called.
  • If the reject function is called, the function resolve will not be called.
  • If there is more code after resolve or reject, it will also be called.
new Promise((resolve, reject) => {
  resolve('resolve 1');
  resolve('resolve 2');
  reject('reject 1');
  reject('reject 2');
  console.log('hey');
}).then((data) => console.log(data));
// It shows "hey" followed by "resolve 1"
// Look into micro/macrotasks to understand why order is like so.

new Promise((resolve, reject) => {
  reject('reject 1');
  reject('reject 2');
  resolve('resolve 1');
  resolve('resolve 2');

  console.log('hey');
})
  .then((data) => console.log(data))
  .catch((e) => console.log(e));
// It shows "hey" followed by "reject1"
// Look into micro/macrotasks to understand why order is like so.
  • If you throw an error instead of calling reject, nothing else will be executed (this is a throw rule though, not specific to Promises).
new Promise((resolve, reject) => {
  reject(new Error('reject 1'));
  console.log('hey');
}).catch((e) => console.log(e.message));
// It shows "hey" followed by "reject1"

new Promise((resolve, reject) => {
  throw new Error('reject 1');
  console.log('hey');
}).catch((e) => console.log(e.message));
// It shows "reject1"
  • The function passed to the Promise is called immediately when the Promise is created.
  • After the Promise is settled, it will not change anymore.
// Write this code in the browser's console
const p = new Promise((resolve, reject) => {
  setTimeout(() => resolve("time's up"), 5000);
});

// If you write the code below in less than 5 seconds,
// you will have to wait for the timeout.
p.then((data) => console.log(data));
  • Calling p.then().then() is different than calling p.then(), p.then()
const p = new Promise((resolve) => {
  resolve(1);
});

p.then((v) => v + 1).then(console.log); // 2

p.then((v) => v + 1);
p.then(console.log); // 1

The p Promise object doesn't change after settled. So if you don't chain the Promise, the then on a different Promise chain is not accounted for.

This is confusing because the jQuery pattern used by many libraries works on top of a mutable object. So when using the jQuery pattern, chaining or not is the same. It's not the same with a Promise because after the Promise is settled, the Promise becomes immutable.

Flattening Promises

If you call a Promise inside another promise, both Promises will be considered the same:

var p1 = Promise.resolve(1);
var p2 = Promise.resolve(p1);
console.log(p1 === p2); // true

Questions

What's the output?

new Promise((resolve, reject) => {
  resolve('res1');
  resolve('res2');
  console.log('boop');
}).then(console.log);

What's the output?

new Promise((resolve, reject) => {
  reject('rej1');
  reject('rej2');
  console.log('boop');
}).then(null, console.log);

What's the output?

new Promise((resolve, reject) => {
  resolve('ok');
  reject('nok');
  console.log('boop');
}).then(console.log, console.log);

What's the output?

const p = new Promise((resolve) => {
  resolve(Math.random());
});

p.then(console.log); // 1
p.then(console.log); // 2

What's the output?

const p = new Promise((resolve) => resolve(1));
p.then((v) => v + 1).then(console.log);

What's the output?

const p = new Promise((resolve) => resolve(1));

p.then((v) => v + 1);
p.then(console.log);

What's the output?

var p1 = Promise.resolve('banana');
var p2 = Promise.resolve(p1);

console.log(p1 === p2);