Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Improve Your JavaScript With the Power of Promises

DZone's Guide to

Improve Your JavaScript With the Power of Promises

Stuck in a callback rut? Want to add super powers to your JavaScript applications?

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Whenever I’m building something, the first language I look to is JavaScript. With all the packages available in Node.js, there’s always a way to get something done. But, like a lot of developers. one of the things that makes me wary about using JavaScript is callback hell. It’s pretty easy to avoid this though, using promises. The library I go for, whether on the browser, or in a Node.js application is q.js   

Let’s take a look at an example. Say that you’re building an application that needs to query a database. You’ll need to first connect to the database and then run your query once you know that you’re connected.  The “normal” JavaScript approach to this would be using callbacks as follows: 

//this code assumes you’ve installed the mysql node package using npm install mysql

var mysql = require('mysql');

function connectAndQuery() {
        var connection = mysql.createConnection({
            host: ‘localhost',
            user: ‘user',
            password: ‘password',
            database: ‘mydb'
        });
        connection.connect(function(err) {
            if (err) {
                console.error('error connecting: ' + err.stack);
            }
            //now that we have a connection we can run the query
               var query = “select solution from mytable”;
               connection.query(query, function(err, rows, fields) {
            if (err) {
                console.log("Error: " + err);
            }

            console.log('The solution is: ', rows[0].solution);
            console.log("got results");

         });
     }


It’s ok, but it’s terrible for readability. Imagine the difficulty in a new developer joining your team and understanding this code? Of course we could split each part into it’s own function, but there’s still a lot of callback-y stuff in there.  Let’s make the code run synchronously using Q. You’ll need to include q.js in your application. For a node application, this is done by running 

npm install q —save

With the package installed, you just need to include the library in your code: 

var q = require(‘q’); 

We’ll break out the connection code into a separate function. There are four main steps that you need to keep in mind for adding promises to your code: 

  1. Create a deferred object using q.

           var deferred = q.defer();

  2. Return the promise at the end of the function  

           return deferred.promise; 

  3. When your function is ready to return a value, use the resolve function and pass through the return value as a parameter 

           deferred.resolve(result);

  4. If you run into an error, you need to use the reject function to let the caller know that something went wrong 

           deferred.reject(err);

It looks a bit foreign at first, but broken into it’s constituent parts, it makes sense. You’re returning a promise to the caller, while you will either resolve with good news at some point in the future, or else throw an error.

function connect() {
        var deferred = q.defer();
        connection = mysql.createConnection({
            host: ‘localhost',
            user: ‘user',
            password: ‘password',
            database: ‘mydb'
        });
        connection.connect(function(err) {
            if (err) {
                console.error('error connecting: ' + err.stack);
                deferred.reject(err);
            }
            //console.log('connected as id ' + connection.threadId);
            deferred.resolve({});
        });
        return deferred.promise;
    }

Now let’s look at the calling code, which will wait until the connection is established before continuing. It does this by calling the function and using the .then() function to take the next step after completion. The then function takes a function as it’s parameter. If the promise resolved with any data (connect() doesn’t!) then you would have access to it in the data parameter. 

connect()
.then(function(data){
     //connection is now made, you can continue
});

To catch the error case, you simply need to add a catch function at any point:

connect()
.then(function(data){
     //connection is now made, you can continue
})
.catch(function (error) {
    // Handle any error from all above steps
});

Of course promises make more sense when there is a sequence of events, so let’s look at making a query after the connection is established

Let’s make running a query into a function that returns a promise 

function runQuery(query) {
        var deferred = q.defer();
        connection.query(query, function(err, rows, fields) {
            if (err) {
                console.log("Error: " + err);
                deferred.reject(err)
            }

            //console.log('The solution is: ', rows[0].solution);
            //console.log("got results");
            deferred.resolve(rows);
        });
        return deferred.promise;
    }

Now we can chain the functions as follows 

connect()
.then(function(data){
     //connection is now made, you can continue
     return runQuery(“select solution from mytable”);
})
.then(function(rows){
    console.log(“The query got “ + rows.length + “ results”);
})
.catch(function (error) {
    // Handle any error from all above steps
});

As you can see, you just need to return the promise to keep the chain going. 

The catch function will catch any rejection from all steps previous. 

There’s a few other neat tricks that you can do with Q.js, but if you’re new to promises that should be enough to get you started. 

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
javascript ,q.js ,promises

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}