/ JavaScript Series

JavaScript Series: Asynchronous Code - Control The Chaos

You learned already how to write asynchronous code and as promised it didn’t get too complicated. We’ve touched the basics but the rules apply to a wider range of APIs. It’s time to learn how to control this chaos.

Promises to the rescue

Promises help you control asynchronous behaviour. They help you streamline things, make things behave and execute the way you tell them to. Let’s start off with a simple example:


  Promise
    .resolve({ firstName: 'Charles', secondName: 'Xavier' })
    .then(function (person) {
      console.log(person);
    });

What happened? We created a Promise that always resolves (succeeds). Once it succeeded, we took data it returned and printed out in the console. Promises can either resolve or reject.

2w9DG4erU

Things don’t always succeed, though. Servers crash, request timeouts occur. What can we do to address potential failures?

  
  Promise
    .reject(new Error('Server crashed'))
    .then(function (person) {
      console.log(person);
    })
    .catch(function (error) {
      console.error(error.message);
    });

The code above catches errors that might have occurred when we requested data. As you can see we expected success but prepared for errors.

2wags0Jq6

This probably makes sense already, what else can we do?

Promises not only let you react to success and failure, they also can act as sort of a pipeline for your data. Thanks to method chaining, you can use tiny functions that morph your data step by step.


  Promise
    .resolve({ firstName: 'Charles', secondName: 'Xavier' })
    .then(function (person) {
      return person.firstName + ' ' + person.secondName;
    })
    .then(function (fullName) {
      console.log(fullName);
    })
    .catch(function (error) {
      console.error(error.message);
    });

Did I mention you can chain callbacks as many times as you want? If your tiny functions throw an error at any point, the catch callback will handle it. Doesn’t it look simple?

pbLogIwuP

It doesn’t look like a real-world example, though, does it?

Promises in the wild

A typical callback based API that retrieves User and then queries his Account Balance by user’s id might look more or less like the following code:


  function findUser(username, callback) {
    setTimeout(function () {
      callback({ userId: 1, username: username, firstName: 'Charles', secondName: 'Xavier' });
    }, 875);
  }

  function getAccountBalance(userId, callback) {
    setTimeout(function () {
      callback({ userId: userId, balance: 1500 });
    }, 1250);
  }

  findUser('professor-x', function (userData) {
    getAccountBalance(userData.userId, function (accountBalanceData) {
      console.log('Account Balance equals = ' + accountBalanceData.balance);
    });
  });

Thanks to setTimeout we are able to simulate network latencies (delays). Works as expected:

We can now express the very same thing with Promises.

  
  function findUser(username) {
    return new Promise(function (resolve, reject) {
      setTimeout(function () {
        resolve({ userId: 1, username: username, firstName: 'Charles', secondName: 'Xavier' });
      }, 875);
    });
  }

  function getAccountBalance(userId) {
    return new Promise(function (resolve, reject) {
      setTimeout(function () {
        resolve({ userId: userId, balance: 1500 });
      }, 1250);
    });
  }

  findUser('professor-x')
    .then(function (userData) {
      return getAccountBalance(userData.userId);
    })
    .then(function (accountBalanceData) {
      console.log('Account Balance equals = ' + accountBalanceData.balance);
    });

Doesn’t it look elegant? The first two functions got rid of the extra “callback" parameter. They might have increased in the number of lines but what really matters is how you can benefit from it. The lines at the bottom read "Find professor-x user and then get account balance by his userID and then print the balance".

IJFrSrYV87

Promises bring a lot of order to the chaos. Learn to use them, experiment, try things out. I omitted error handling on purpose but please remember to always catch potential failures.

Promises can do a lot more than this. As a homework, please have a read about Promise.all and Promise.race

Good luck!