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

Making Promises for a Better World

DZone's Guide to

Making Promises for a Better World

Dealing with callbacks in Node.js can be a bit cumbersome, and while Promises have been with us for some time, sometimes it's helpful to review the basics and some options. Read on to find out more.

· Web Dev Zone
Free Resource

Get the senior executive’s handbook of important trends, tips, and strategies to compete and win in the digital economy.

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.

Read this guide to learn everything you need to know about RPA, and how it can help you manage and automate your processes.

Topics:
design ,function ,pattern ,callback ,promises ,node.js ,code

Published at DZone with permission of Can Ho, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}