Promises Tutorial by Irakli Nadareishvili
  • Introduction
  • Turning Callbacks Into Promises
  • Chain Parallel with Sequential
  • Conditions and Promises
  • Keeping Promise Chains Flat
  • Async and Await
  • Async/Await & Express, Mocha etc.
  • One More Thing (Go)...
  • One More Thing (Python)...
  • One More Thin (Nim)…
Powered by GitBook
On this page

Was this helpful?

Turning Callbacks Into Promises

PreviousIntroductionNextChain Parallel with Sequential

Last updated 5 years ago

Was this helpful?

As an experienced JavaScript developer, you probably already have a whole bunch of asynchronous code that uses callbacks. To get quickly familiar with Promises, let's see how you would "Promisify" a legacy, callback-using function and use it in your application.

Side note: source files of all examples and instructions for how to execute, are located at:

As an example of an asynchronous function, let's use one that makes an HTTP call. In the past you probably used the popular request package, with code that may have looked something like the following:

const request = require('request');

const base_url = "https://www.googleapis.com/books/v1/volumes?q=";
const books_api_url = base_url + '"inauthor:martin fowler"';

request(books_api_url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        console.log(body);
    } else {
        console.log(error);
    }
});

This code calls Google Books API to retrieve all books written by Martin Fowler. In order to rewrite the code with Promises, we need a request method that is "promisified" i.e. uses promises instead of asynchronous callbacks. Such version of the request package does actually exist: and we can quickly rewrite the above code as:

const rp = require('request-promise');

const base_url = "https://www.googleapis.com/books/v1/volumes?q=";
const books_api_url = `${base_url}"inauthor:martin fowler"`;

rp(books_api_url)
    .then(function (body) {
        console.log(body);
    })
    .catch(function(err) {
        console.log(err);
    });
const rp = require('request-promise');

const base_url = "https://www.googleapis.com/books/v1/volumes?q=";
const books_api_url = `${base_url}"inauthor:martin fowler"`;

rp(books_api_url)
    .then(body => {
        console.log(body);
    })
    .catch(err => {
        console.log(err);
    });

When promises support is already available you can just chain asynchronous calls with the .then syntax, as showed earlier. However, it won't always be the case and when it is not, or when you want to promisify your own legacy code, we should know how to do it. It's actually quite straightforward. Let's see how we could promisify an http call by wrapping the corresponding function of the classic request package, ourselves:

const request = require('request');

function http_get_p(url) {
    return new Promise((resolve, reject) => {
        request(url, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve(body);
            } else {
                reject(error);
            }
        });
    });
}

const base_url = "https://www.googleapis.com/books/v1/volumes?q=";
const author = "Martin Fowler";
const books_api_url = `${base_url}"inauthor:${author}"`;

http_get_p(books_api_url)
    .then(function (body) {
        console.log(body);
    })
    .catch(function(err) {
        console.log(err);
    });

What just happened? Basically, for a function to be "then-able" (participate in then()-based async workflow) it must return a function with reject() and resolve() methods. The Promise class, in the example above, is such function. Our asynchronous functions return an instance of the Promise class, invoking resolve() or reject() based on the success or failure of its execution, as shown in the above example.

To conclude this chapter, let's extend our example code with additional logic: when we retrieve author's books we resolve with a different message based on their popularity and error-out if they are not a published author, at all:

const request = require('request');

function http_get_p(url) {
  return new Promise((resolve, reject) => {
    request(url, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        resolve(body);
      } else {
        reject(error);
      }
    });
  });
}

const base_url = "https://www.googleapis.com/books/v1/volumes?q=";

function is_author_popular(author) {
  const books_api_url = `${base_url}"inauthor:${author}"`;

  return new Promise((resolve, reject) => {
    http_get_p(books_api_url).then(body => {
        const json_body = JSON.parse(body);    
        if (json_body.totalItems > 10) {
          resolve(`${author} is a very active author!`);
        } else if (json_body.totalItems > 0) {
          resolve(`${author} has published less than 10 books.`);
        } else {
          reject(`${author} is not a published author!`);
        }
    });
  })
}

let author = "Martin Fowler";
is_author_popular(author).then((result) => {
  console.log(result); // only success scenario executes here
}).catch((err) => {
  console.log('ERROR: ' + err);
});

author = "Irakli Nadareishvili";
is_author_popular(author).then((result) => {
  console.log(result); // only success scenario executes here
}).catch((err) => {
  console.log('ERROR: ' + err);
});

author = "Bogus Author";
is_author_popular(author).then((result) => {
  console.log(result); // only success scenario executes here
}).catch((err) => {
  console.log('ERROR: ' + err);
});

This code, if successfully executed should return:

Irakli Nadareishvili has published less than 10 books.
Martin Fowler is a very active author!
ERROR: Bogus Author is not a published author!

Please note: the order of the lines in the response may vary, since our lookups are asynchronous and parallelized.

or using :

Side note: in the last example we started using arrow functions syntax when function is an argument being passed to another function. Arrow functions don't just shorten the syntax, but they also do not bind their own: this, arguments, super, or new.target, which makes them great candidates for the use-case. In ES6 syntax it is very common to use arrow function syntax when function is acting as an argument. Further in this documentation we will use arrow-functions for anonymous functions everywhere, since they make code less error-prone and more readable. If you are new to arrow-functions, we recommended reviewing to them and Dmitri Pavlutin's blog post.

A lot of times, you will find already-"promisified" versions of the packages that you have previously used with callbacks. It has also become quite common to see packages which had been written with promises in mind, to begin with. Case in point is the module - isomorphic implementation for the native replacement of the XMLHttpRequest object, in modern browsers. Fetch API and the fetch module have supported promises from their inception.

https://github.com/inadarei/promises123-code
request-promise
ES6 fat-arrow functions
Mozilla's guide
When Not To Use
Fetch