in

JavaScript: Promises 🤝🏽

Javascript logo

Promises are a new set of functionality in Javascript that provide an alternative to repetitive nested callback function (AKA callback hell). You can read more about promises here:

Basic Syntax

Constructor

Promise()

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

let promiseObj = new Promise( tetherFunction );

tetherFunction

The tetherFunction is the code to be executed by the constructor, and is written by the programmer.

let promiseObj = new Promise( tetherFunction(resolve, reject) {
  // executor 
});

Where resolve and reject are defined by the Javascript engine. There can be only result between these two, and when that condition is met, the promise has been resolved (either fulfilled or not).

Here is an alternate syntax with arrow functions.

let p = new Promise((resolve, reject) => {
  // executor 
});

Here is a sample promise.

var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
  if (keepsHisWord) {
    resolve("The man likes to keep his word");
  } else {
    reject("The man doesnt want to keep his word");
  }
});
console.log(promise1);
firefox console

The promise begins in a state called pending and then it is either fulfilled or rejected.

In this case the state will be rejected if the status of keepsHisWord is false, null or undefined.

We can use simple if else statements to match the desired value of the promise.

And here is another sample Promise with arrow functions this time:

let p = new Promise((resolve, reject) => {
  let a = 1+1
  if (a == 2) {
    resolve('Success');
  } else {
    reject('Failed');
  }
});

Methods

The methods are triggered when the state of the promise moves from pending to fulfilled/rejected.

.then

The most important, fundamental one is .then.

The syntax is:

promise.then(
  function(result) { /* handle a successful result */ },
  function(error) { /* handle an error */ }
);

The first argument of .then is a function that runs when the promise is resolved, and receives the result.

The second argument of .then is a function that runs when the promise is rejected, and receives the error.

For instance, here’s a reaction to a successfully resolved promise:

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve runs the first function in .then
promise.then(
  result => alert(result), // shows "done!" after 1 second
  error => alert(error) // doesn't run
);

If we’re interested only in successful completions, then we can provide only one function argument to .then:

let promise = new Promise(resolve => {
  setTimeout(() => resolve("done!"), 1000);
});

promise.then(alert); // shows "done!" after 1 second

.catch

If we’re interested only in errors, then we can use null as the first argument: .then(null, errorHandlingFunction). Or we can use .catch(errorHandlingFunction), which is exactly the same:

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// .catch(f) is the same as promise.then(null, f)
promise.catch(alert); // shows "Error: Whoops!" after 1 second

The call .catch(f) is a complete analog of .then(null, f), it’s just a shorthand.

.finally

Finally will run regardless of whether the promise was kept or not. It is good for cleanup, like stopping a loading indicator.

.all

This will run when all of the promises specified in an array have been fulfilled.

.race

This will return the first Promise that completes it’s task.

const recordVideoOne = new Promise((resolve, reject) => {
  resolve('Video 1 Recorded')
})

const recordVideoTwo = new Promise((resolve, reject) => {
  resolve('Video 2 Recorded')
})

const recordVideoThree = new Promise((resolve, reject) => {
  resolve('Video 3 Recorded')
})

Promise.race([
  recordVideoOne,
  recordVideoTwo,
  recordVideoThree
]).then(message => {
  console.log(message)
})

Examples

Example 1: A simple promise

let p = new Promise((resolve, reject) => {
  let a = 1+1
  if (a == 2) {
    resolve('Success');
  } else {
    reject('Failed');
  }
});

Going back to a good example above, what would some callback functions look like on this?

p.then((message) => {
  console.log('This is in the .then ' + message);
}).catch((message) => {
  console.log('This is in the .catch ' + message);
})

If the promise resolves then we will get the .then message.

If the promise rejects then we will get the .catch message.

Example 2: Replacing a callback function

Let us first look at how this would be structured as a callback function.

function watchTutorialCallback(callback, errorCallback) {
  let userLeft = false
  let userWatchingCatMeme = false

  if (userLeft) {
    errorCallback({
      name: 'User Left', 
      message: ':('
    })
  } else if (userWatchingCatMeme) {
    errorCallback({
      name: 'User Watching Cat Meme',
      message: 'WebDevSimplified < Cat' 
    })
  } else {
    callback('Thumbs up and Subscribe')
  }
}

We have defined the callback function, now we can call it.

watchTutorialCallback(message => {
  console.log(message)
}, error => {
  console.log(error.name + ' ' + error.message)
})

Now we can convert all this to a promise.

function watchTutorialPromise() {
  let userLeft = false
  let userWatchingCatMeme = false
  return new Promise((resolve, reject) => {
    if (userLeft) {
      reject({
        name: 'User Left', 
        message: ':('
      })
    } else if (userWatchingCatMeme) {
      reject({
        name: 'User Watching Cat Meme',
        message: 'WebDevSimplified < Cat' 
      })
    } else {
      resolve('Thumbs up and Subscribe')
    }
  })
}

Note that we have removed the callbacks.

watchTutorialPromise().then(message => {
  console.log(message)
}).catch(error => {
  console.log(error.name + ' ' + error.message)
})

This is a superior syntax because if we want to stack up multiple callbacks, we would just tack on additional .then methods, instead of continuously nesting callbacks (callback hell).

Example 3: .all method

The .all method is what executes when all promises have been resolved.

const recordVideoOne = new Promise((resolve, reject) => {
  resolve('Video 1 Recorded')
})

const recordVideoTwo = new Promise((resolve, reject) => {
  resolve('Video 2 Recorded')
})

const recordVideoThree = new Promise((resolve, reject) => {
  resolve('Video 3 Recorded')
})

Promise.all([
  recordVideoOne,
  recordVideoTwo,
  recordVideoThree
]).then(messages => {
  console.log(messages)
})
All of the messages are returning together.
Frosty CMS Logo

Frosty CMS: Seeding the Database 🌱

Javascript logo

JavaScript: Async Await