Promise to Async and Await

Yesterday on snail mail I received a pin from the Red Cross. Apparently I have given blood 16 times so far. That adds up to two gallons of blood. Planning on continue to donate in the foreseeable future.

As you can tell based on the code we will be using for this post, I am a fan of Ian Fleming’s spy novels staring James Bond. I read all his books when I was growing up. Have watched all the James Bond movies and own a DVD collection of all the titles that have been released of 007.

To start we have a set of functions written in JavaScript using promises. The idea is to convert them to use async and await function decorators. This is an edited exercise from a course by Mosh Hamedani on JavaScript that I am currently taking.

The code to convert follows:

// **** ****
const delay = 4000;

// **** callback based approach ****
getAgent(7, agent => {
  console.log("<<< agent: ", agent); // **** must have license to kill **** if (agent.licenseToKill) { getTopWeapons(weapons => {
      console.log("<<< top weapons: ", weapons); sendEmail(agent.email, weapons, () => {
        console.log("<<< email sent!");
      });
    });
  }
});

function getAgent(id, callback) {
  console.log("getAgent <<< getting agent..."); setTimeout(() => {
    callback({
      id: 7,
      name: "James Bond",
      licenseToKill: true,
      email: "james.bond@mi6.gov"
    });
  }, delay);
}

function getTopWeapons(callback) {
  console.log("getTopWeapons <<< getting weapons..."); setTimeout(() => {
    callback(["Walther PPK", "Beretta 418", "Winchester .308 target rifle"]);
  }, delay);
}

function sendEmail(email, movweaponsies, callback) {
  console.log("sendEmail <<< sending email..."); setTimeout(() => {
    callback();
  }, delay);
}

The callback based approach is illustrated at the top of the code. The three associated functions follow. I have placed a few comments to be able to easily follow the logic. The issue is that we make use of callbacks and the format for their use is not that obvious.

A run of the code follows:

[nodemon] restarting due to changes...
getAgent <<< getting agent...
[nodemon] starting `node exercise.js`
getAgent <<< getting agent...
<<< agent:  { id: 7,
  name: 'James Bond',
  licenseToKill: true,
  email: 'james.bond@mi6.gov' }
getTopWeapons <<< getting weapons...
<<< top weapons:  [ 'Walther PPK', 'Beretta 418', 'Winchester .308 target rifle' ]
sendEmail <<< sending email...
<<< email sent!
[nodemon] clean exit - waiting for changes before restart

We start by pretending we are looking for the top weapons used by a specified agent. If the agent has a license to kill (that is the title of one of the movies) then we look up his top weapons and send him a message indicating that they are ready for his next mission. The actual text for the message is not displayed.

Now let’s look at the code using the async and await decorators.

// // **** ****
// getAgent(7, agent => {
//   console.log("<<< agent: ", agent); // if (agent.licenseToKill) { // getTopWeapons(weapons => {
//       console.log("<<< top weapons: ", weapons); // sendEmail(agent.email, weapons, () => {
//         console.log("<<< email sent!");
//       });
//     });
//   }
// });

// **** ****
const delay = 4000;

/*
 * Approach using async and await.
 * Wrapper async to use await.
 */
async function notifyAgent(agentID) {
  // **** ****
  console.log("notifyAgent <<< getting agent...");
  const agent = await getAgent(agentId);
  console.log("notifyAgent <<< agent: ", agent);

  // **** must have license to kill ****
  if (agent.licenseToKill) {
    // **** ****
    console.log("notifyAgent <<< getting top weapons...");
    const weapons = await getTopWeapons();
    console.log("notifyAgent <<< top weapons: ", weapons);

    // **** ****
    console.log("notifyAgent <<< sending email...");
    await sendEmail(agent.email, weapons);
    console.log("notifyAgent <<< mail sent!"); } } // **** notify agent **** const agentId = 7; notifyAgent(agentId); /* * */ function getAgent(id) { return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        id: id,
        name: "James Bond",
        licenseToKill: true,
        email: "james.bond@mi6.gov"
      });
    }, delay);
  });
}

/*
 *
 */
function getTopWeapons() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(["Walther PPK", "Beretta 418", "Winchester .308 target rifle"]);
    }, delay);
  });
}

/*
 *
 */
function sendEmail(email, weapons) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, delay);
  });
}

In order to use await we need to have it embedded in code that is asynchronous. For that we declare the notifyAgent() function and decorate it with async. The code is a lot simpler to write and follow. We get the agent record from a database. With the record we decide if the agent has a license to kill. If so we retrieve his top weapons. Once we have them we send the agent an email message indicating the weapons are ready for his next assignment.

The run for the code follows:

[nodemon] starting `node solution.js`
notifyAgent <<< getting agent...
notifyAgent <<< agent:  { id: 7,
  name: 'James Bond',
  licenseToKill: true,
  email: 'james.bond@mi6.gov' }
notifyAgent <<< getting top weapons...
notifyAgent <<< top weapons:  [ 'Walther PPK', 'Beretta 418', 'Winchester .308 target rifle' ]
notifyAgent <<< sending email...
notifyAgent <<< mail sent!
[nodemon] clean exit - waiting for changes before restart

The code behaves in the same way as the original one.

If you are interested in this code you can find the two files in my GitHub repository.

If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.