Making Promises For a Better World

Do you think that we can make our world better by making and keeping our promises? If you agree, keep reading! Just kidding. Actually, in this post, we’re going to see how we can build better software by making promises in Node.js.

Node is built with callback design pattern in mind. Large part of its APIs accepts a callback function as a second parameter. Callbacks are very easy to grasp. They have become the default way of handling async data flows in JavaScript. But its simplicity comes at a price. While callback function is fine for simple cases, it may causes a lot of issues in complex cases:

  • Callback hell
  • Inconsistent behavior ( releasing Zalgo) )
  • Misbehaving callbacks
    • Callbacks may be called many times
    • Callbacks may never be called
    • Callbacks may be called too early

And, Promises came to save us from callbacks. But, in Node, how can we make Promises from the callback world? By promisifying!

Almost all Promise libraries support an extra API allowing us to make a Promise version from callback version:

What “making a promise” functions do is to take an asynchronous operation that conforms to error-first style (or Node style) and wrap that into a Promise. Note that “making a promise” functions can’t convert a synchronous operation into a Promise based operation.

In following examples, we’re going to use bluebird to make Promises. By default, bluebird append the suffix “Async” to the callback based operation name when creating a Promise based version: method1 –> method1Async

before

const fs = require('fs')

fs.readFile('./data.zip', (err, content) => {
  if (err) {
      return errorHandler(err)
  }
  console.log(content)
})

after

const Promise = require("bluebird")
const fs = Promise.promisifyAll(require("fs"))

fs.readFileAsync("./data.zip")
  .then(content => console.log(content))
  .catch(errorHandler)

before

import mysql from "mysql";
const pool = mysql.createPool({..});

module.exports.execute = function(statement, params) {
    pool.getConnection(function(e, connection) {
        if(e) {
            return errorHandler(e);
        } else {            
            connection.query(statement, (e, results, fields) =>{
                connection.release();
                if(e) {
                    return errorHandler(e);
                } else {
                    console.log(results);
                }
            });
        }
    });
}

after

import mysql from "mysql";
import Promise from "bluebird";

Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);

const pool = mysql.createPool(config.dbConnectionPool);

/**
 * API to execute a sql statement against the db.
**/
export function execute (sql, params) {
    return Promise.using(getConnection(), (conn)=>{
        return conn.queryAsync(sql, params);
    });
}

function getConnection(){
    return pool.getConnectionAsync().disposer((conn)=>{      
        conn.release();
    });
}

With Promises, our program is more clear by being closer to synchronous code, reducing nesting blocks and keeping track of less state.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s