Writing Functions that return Promises

Before diving into this article, make sure you understand how to use functions that return promises.

The Promise API was designed for operations that can take a long time and could potentially fail.

If you write a function that does an operation that takes a long time, and/or could potentially fail. Then you can have it return a Promise object

A Promise object allows you to chain to it then() and catch() method calls.

When you create a Promise, you pass a function into the constructor and put the code for your operation in the body of this function

The function should have two parameters, both of which are functions (how confusing is that?!). The first function is usually called 'resolve', which you'll invoke once the long running operation completes successfully (and when you invoke it, you'll pass in the return value of the operation. The second function is usually called 'reject', which you'll invoke if the operation fails.

This is all hard to understand until you see it in action (and then it's still takes time to understand).

Create a file named making-promises.html, and put this code into it:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Making Promises</title>
    <script>

      // Here's a function that could fail, so it returns a Promise object
      function getDog() {
        return new Promise((resolve, reject) => {
          // Here's where you put the code that could fail (and/or take a long time to complete)
          // generate a random number 1 or 0, to determine if it succeeds or fails
          let randomNumber = Math.round(Math.random());
          if (randomNumber == 1) {
            // SUCCESS! - so invoke the resolve() function an pass in the result
            resolve({ name: "Brutus", breed: "Boxer", age: 9 });
          } else {
            // FAILURE! - so throw an error
            reject(new Error("Failed to get dog!"));
          }
        });
      }

      // TODO: call the function that returns a promise

    </script>
  </head>
  <body>
    <h1>Making Promises</h1>
  </body>
</html>

Notice that the getDog() function calls the Promise constructor and immediately returns the resulting Promise object.

A callback function is passed into Promise constructor. In the body of this function, we do all the work that could potentially take a long time, and/or could possibly fail. Under the hood, when JavaScript calls this function, it will pass in two parameters, that are both functions. The first one, resolve(), should be invoked when the operation completes successfully. The second one, reject(), should be invoked if the operation fails.

This code works by randomly generating a 1 or a 0 to determine if the operation succeeds or fails.

If it succeeds (1), then we invoke resolve() and pass in a dog object as a parameter. The Promise will pass this dog object into the callback that you use for then().

If it fails (0), then we instantiate an Error object and pass it as a param when we call reject(). Behind the scenes, the Promise will pass this error object into the callback that your provide for catch().

Now that we have a function that returns a promise, let's try it out by adding this code to the script element:

// Call the function a few times using then() and catch()
getDog().then(dog => console.log(1,dog)).catch(err => console.log(1,err));
getDog().then(dog => console.log(2,dog)).catch(err => console.log(2,err));
getDog().then(dog => console.log(3,dog)).catch(err => console.log(3,err));

Note that I've added numbers to the console logs so that you can see that the code will not necessarily execute in sequence (that's the asynchronous nature of JavaScript).

You could use async and await instead of then() and catch(), like so:

// using async and await
(async () => {
	try{
		const dog = await getDog();
		console.log(4, dog);
	}catch(err){
		console.log(4, err.message);
	}
})()

The anonymous arrow function is declared with the async keyword. And the await keyword is used when calling the function that returns a promise (getDog()), which means that the console log on the next line will not execute until the promise is resolved (meaning that it completed successfully). If the promise is 'rejected', we will end up in the catch block and the err object will be the exact same one the was passed into reject().