The Promise Constructor

Open the project folder

In our starter files, open the index.html page from this lessons folder:

10.Async-JavaScript > 04.The-promise-constructor

Introduction

In the previous lesson, we could use promises because the Fetch API returns a Promise. But what about functions that don’t return a Promise?

To begin, we have an empty starter file. Let’s write a simple custom function, this will set a name property to a data object after a timeout:

let data = {};
function getData() {
  setTimeout(function () {
    data = { name: 'Chris' };
  }, 2000);
}
getData();
console.log(data.name); // undefined

The result in the console is undefined because we access the empty data object immediately, but the data takes two seconds to be set.

This is like a simulated request to a database, we need to handle what happens between requesting the data and receiving it. We know promises are good for this, but this function does not return a promise by default.

For this we can create our own using the promise constructor.

Creating a Promise constructor

Constructors are something we have already used with the new operator. We have looked at:

new Array()
new Object()
new Function()

These all created a new instance of an Object. Promises are objects too, so we can also use the new operator to create a new Promise.

First, comment out this example, except the data variable:

let data = {};

// function getData() {
//   setTimeout(function () {
//     data = { name: 'Chris' };
//   }, 2000);
// }
// getData();
// console.log(data.name);

Then create a new Promise below:

let data = {};

// function getData() {
//   setTimeout(function () {
//     data = { name: 'Chris' };
//   }, 2000);
// }
// getData();
// console.log(data.name);

new Promise()

This constructor takes in a function:

new Promise(function () {

})

Optional parameters

And this function takes two optional parameters. A function to run if the promise resolves, and one for a rejection:

new Promise(function (resolve, reject) {

})

These are names of our choice, but resolve and reject are descriptive. Since this is our own custom Promise, we set when we want the resolve and reject functions to run.

The resolve function

Let’s begin with resolve:

// 1. store promise in variable to call when required:
const promise = new Promise(function (resolve, reject) {
  // 2. for now, simply resolve the promise
  resolve();
});

// 3. use the promise variable to call the then(), catch(), and finally() methods:
promise.then(function () {
  console.log('success');
})
promise.catch(function (error) {
console.log(error);
});

The result in the logs should be success.

This resolve function can also receive a fulfilment value to pass on:

const promise = new Promise(function (resolve, reject) {
  resolve('SUCCESS');
});

And this return value is then available in the following then() function:

const promise = new Promise(function (resolve, reject) {
  resolve('SUCCESS');
});
promise.then(function (response) {
  console.log(response); // SUCCESS
});
promise.catch(function (error) {
  console.log(error);
});

Going back to the earlier example of fetching data we commented out. Copy and paste the setTimeout into the Promise function:

const promise = new Promise(function (resolve, reject) {
  // paste setTimeout from previous example
  setTimeout(function () {
    data = { name: "Chris" };
  }, 2000);
  resolve("SUCCESS");
});

Then move the resolve function to be inside the setTimeout. This will run after the data has been set:

const promise = new Promise(function (resolve, reject) {
  setTimeout(function () {
    data = { name: 'Chris' };
    resolve('SUCCESS');
  }, 2000);
});

This now means the Promise should only be resolved after the data has been set. Test this by logging the data:

promise.then(function (response) {
  console.log(response);
  console.log(data);
});

But what about rejecting?

The reject function

This is our custom promise, and we can make it whatever we want. For this example, we will successfully resolve if the name property has been set, and reject if the data object is empty. Modify the code inside the setTimeout as follows:

const promise = new Promise(function (resolve, reject) {
  setTimeout(function () {
    data = { name: "Chris" };
    if (Object.keys(data).length > 0) {
      resolve("success");
    }
    reject("rejected");
  }, 2000);
});

Recap

If a function by default does not return a Promise, we can create our own. We do this with the Promise() constructor. This can resolve or reject at any point we feel it should, we have total control over the behaviour.