DotNetCurry Logo

Promises in Node.js using Q - Farewell to Callbacks

Posted by: Mahesh Sabnis , on 1/20/2016, in Category Node.js
Views: 10986
Abstract: Promise objects allow us to react to asynchronous events in our Node.js code without worrying about when the events occur. This article demonstrates how to use Promises in Node.js applications.

Writing asynchronous code sometime makes the code unreadable and tedious to maintain. Code containing too many callback functions in asynchronous operations, often results in code complexity where it becomes an additional burden for the developer to keep track of inner-functions and outer-functions accurately. Typically, performing several asynchronous operations in a sequence or in parallel may be an issue.

 

The following pseudo code will provide an idea of how the code gets complex with a long sequence of Callbacks. Later we will see how Promises in Node.js simplifies the code.

someOperation(function(err,res){
  if(err){
      console.error(err)
      return next(err);
      someOtherOperation(function(err){
         if (err) {
            console.error(err)
            return next(err);
            anotherOperation(err) {
                if (err) {
                  return next(err);
                  next();
                });
            });
      }); 
  console.log(JSON.parse(res));
});

In the above pseudo code, someOperation() function is passed with callback function. The callback is an executable code passed as an argument to other code. Going forward, we have another callback to be passed for someOperation() and in turn another one. In all these functions like someOperation(), someOtherOperation() etc. , the callback function passed looks rather confusing to a new user. It looks like an input parameter being passed to the functions. If some error occurs while executing console.log(), then it is a challenge to handle it because the execution will stop immediately and application will stop responding.

Why use Promises?

Promise is relatively an easy implementation for asynchronous operation. The promise object returned from the function represents an operation which is not completed yet, but it guarantees to the caller of the operation that the operation will be completed in future.

Moving to Promises from Callback

To solve the complexity of the pseudo code, we can make use of promises. In simple words, a promise is an abstraction which allows an asynchronous function to return an object known as promise. This object is eventually a result of the operation. Promise has the following states:

· Pending - asynchronous operation is not yet completed.

· Fulfilled - asynchronous operation is completed successfully.

· Rejected - asynchronous operation is terminated with an error.

· Settled - asynchronous operation is either fulfilled or rejected.

· Callback - function is executed if the promise is executed with value.

· Errback - function is executed if the promise is rejected.

The promise object provides then() function with two parameters for fulfillment and rejection as shown in the following syntax:

promise.then([onFulfilled],[onRejected])

onFulfilled and onRejected are called when the promise is resolved. This means that the asynchronous operation is completed.

The code in our pseudo example can be simplified as shown in following manner using promises:

var promise = someOperation();
promise.then(console.log,console.error).catch(function(err)){}; 

In the above case, promise represents an asynchronous operation. If the promise is fulfilled, console.log will be executed, else in case of a reject state, console.error will be executed. then is not just a function but this represents the outcome of the asynchronous operation. If there is any error with promise fulfillment then catch() function is executed. This provides the same effect of try-catch-finally implementation.

To implement Promises in Node.js, we have several libraries. I like two in particular:

· Bluebird: This is a popular library for implementing promises in Node.js. This provides APIs for performing promisification. This means that it converts standard non-promise aware APIs to promise returning APIs. More information of this library can be found from this link.

· Q: This is another popular library for implementation of promises in Node.js. This library provides an easy approach of then().catch() functions for using promises. More information for Q can be read from this link.

We will use Q for our current application. To see how to implement Promises using Bluebird, check this article Using Promises in Node.js Applications using Bluebird .

Promises in Node.js

We will be using Visual Studio Code (VSCode) for implementing our application. This is a new FREE editor provided by Microsoft for building and debugging modern web and cloud applications. This editor is available on Linux, Mac OSX in addition to Windows and can be downloaded from this link. This IDE is especially used for developing applications using Node.js, ASP.NET 5 (now called as ASP.NET Core 1.0), and other JavaScript libraries and frameworks. This editor is based on Electron.

Step 1: Create a folder of the name Promise_Art on your drive. Open VSCode and using File > Open Folder, open the Promise_Art folder. Using VSCode, add a new file in it of the name app.js. Right-click on this file and select Open in Command Prompt option, to open the command prompt. We will install the necessary packages from this command prompt.

Step 2: Run following commands from the command prompt.

npm install -g tsd

This will install a Typescript definition for VSCode so that we can get the help of the intellisense for various packages.

tsd query node --action install

This command provides Node.js intellisense for the application.

tsd query q --action install

This will install Q in the application.

Step 3: The application in this article uses http module to make a call to external Web API service. An asynchronous way of making call to external Web API service can be read from http://www.dotnetcurry.com/nodejs/1225/call-external-service-using-nodejs . The current application will modify the code using promises with Q package. (Note: The article uses Web API, so if you are new to Web API but are an ASP.NET developer, this link will help.). You can replace Web API with any other REST service of your choice.

 

Step 4: In this VSCode application, add a new JavaScript file of the name module.js. This is a Node.js module which will contain logic to make call to an externally hosted Web API service. Add the following code in module.js

//1.
var q = require('q');
//2. 
var http = require('http');
//3.
module.exports = {
    //4.
    getData: function (options) {
        var emp = "";
        
        //5.
        var defer = q.defer();
        var request;
        if (!options) {
            //6.
            defer.reject('Please specify the url to receive data');
        } else {
            //7.
            request = http.request(options  , function (response) {
                response.setEncoding('utf-8');
                response.on('data', function (chunk) {
                    emp += chunk;
                });
                //8.
                response.on('end', function () {
                    try {
                        var receivedJson = JSON.parse(emp);
                        
                        defer.resolve(receivedJson);
                    } catch (error) {
                        //9.
                        defer.reject(error);
                    }
                });
            });
        }
        //10.
        request.end();
        //11.
        return defer.promise;
    }
};

The above code has following specifications:

(Note: Line numbers matches with the comments applied on the code.)

1. Load the Q module for using promises APIs.

2. Load the http module to make call to externally hosted Web API.

3. The custom New.js module definition.

4. The getData() function contains business logic. The options parameter contains information of the external Web API service.

5. The defer() object is used for observing the state of the promise to check whether it is rejected or resolve.

6. The code checks the value of options if it is not passed then the reject() is called.

7. A http request to the externally hosted Web API service by passing the options parameter to it. The data is received on the event and stored in the emp array.

8. The http operation is over and defer is resolved, this means the promise state is fulfilled.

9. If any error occurs then the promise will be rejected.

10. The http request comes to an end.

11. The promise object is returned from the getData() function to the caller of this module.

Step 5: In the app.js add the following code:

//1.
var callModule = require('./module');

//2.
var serverDetails = {
    host: 'localhost',
    port: '39916',
    path: '/api/EmployeeInfoAPI',
    method: 'GET'
};

var emp = [];
//3.
callModule.getData(serverDetails).then(function (response) {
     
    console.log("EmpNo\t EmpName\tSalary\tDeptName\tDesignation");
    console.log();
    for (var i = 0; i < response.length; i++) {
        console.log(response[i].EmpNo + "\t" + response[i].EmpName + "\t\t" + response[i].Salary + "\t" + response[i].DeptName + "\t\t" + response[i].Designation);
    }
}).catch(function (err) {
    console.log(err);
});

console.log('Done');

The above code has following specifications:

(Note: Line numbers matches with the comments applied on the code.)

1. Load the module created in step 4.

2. The JavaScript object of name serverDetails defining details of the externally hosted Web API. This information contains host name, port, the service path and the type of http operation being performed.

3. The call to the getData() function of the module. This function is passed by the serverDetails. Since this function returns promise object, we are calling then() function which listens to the state of the promise object and prints Employees received when the promise is resolved. If the promise has errors then the catch() function will be called.

Step 6: To run the application, enter the following command from the command prompt which we opened in Step 2

node app

The following result will be displayed

nodejs-promise-resolved

If the external hosted Web API service information is incorrect, then the following result will be as shown:

nodejs-promise-rejected

Conclusion:

Promises in Node.js is useful to eliminate coding complexity caused due to asynchronous code. It makes successful call and error handling more easy using then() and catch() functions respectively.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!