Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Node.js Blueprints

You're reading from   Node.js Blueprints Develop stunning web and desktop applications with the definitive Node.js

Arrow left icon
Product type Paperback
Published in Jun 2014
Publisher
ISBN-13 9781783287338
Length 268 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Krasimir Stefanov Tsonev Krasimir Stefanov Tsonev
Author Profile Icon Krasimir Stefanov Tsonev
Krasimir Stefanov Tsonev
Arrow right icon
View More author details
Toc

Table of Contents (14) Chapters Close

Preface 1. Common Programming Paradigms FREE CHAPTER 2. Developing a Basic Site with Node.js and Express 3. Writing a Blog Application with Node.js and AngularJS 4. Developing a Chat with Socket.IO 5. Creating a To-do Application with Backbone.js 6. Using Node.js as a Command-line Tool 7. Showing a Social Feed with Ember.js 8. Developing Web App Workflow with Grunt and Gulp 9. Automate Your Testing with Node.js 10. Writing Flexible and Modular CSS 11. Writing a REST API 12. Developing Desktop Apps with Node.js Index

Asynchronous programming

As we already learned, in nonblocking environments, such as Node.js, most of the processes are asynchronous. A request comes to our code, and our server starts processing it but at the same time continues to accept new requests. For example, the following is a simple file reading:

fs.readFile('page.html', function (err, content) {
  if (err) throw err;
  console.log(content);
});

The readFile method accepts two parameters. The first one is a path to the file we want to read, and the second one is a function that will be called when the operation finishes. The callback is fired even if the reading fails. Additionally, as everything can be done via that asynchronous matter, we may end up with a very long callback chain. There is a term for that—callback hell. To elucidate the problem, we will extend the previous example and do some operations with the file's content. In the following code, we are nesting several asynchronous operations:

fs.readFile('page.html', function (err, content) {
  if(err) throw err;
  getData(function(data) {
    applyDataToTheTemplate(content, data, function(resultedHTML) {
      renderPage(resultedHTML, function() {
        showPage(function() {
          // finally, we are done
        });
     });
  });
  });
});

As you can see, our code looks bad. It's difficult to read and follow. There are a dozen instruments that can help us to avoid such situations. However, we can fix the problem ourselves. The very first step to do is to spot the issue. If we have more than four or five nested callbacks, then we definitely should refactor our code. There is something very simple, which normally helps, that makes the code shallow. The previous code could be translated to a more friendly and readable format. For example, see the following code:

var onFileRead = function(content) {
  getData(function(data) {
    applyDataToTheTemplate(content, data, dataApplied);
  });
}
var dataApplied = function(resultedHTML) {
  renderPage(resultedHTML, function() {
    showPage(weAreDone);
  });
}
var weAreDone = function() {
  // finally, we are done
}
fs.readFile('page.html', function (err, content) {
  if (err) throw err;
    onFileRead(content);
});

Most of the callbacks are just defined separately. It is clear what is going on because the functions have descriptive names. However, in more complex situations, this technique may not work because you will need to define a lot of methods. If that's the case, then it is good to combine the functions in an external module. The previous example can be transformed to a module that accepts the name of a file and the callback function. The module is as follows:

var renderTemplate = require("./renderTemplate.js");
renderTemplate('page.html', function() {
  // we are done
});

You still have a callback, but it looks like the helper methods are hidden and only the main functionality is visible.

Another popular instrument for dealing with asynchronous code is the promises paradigm. We already talked about events in JavaScript, and the promises are something similar to them. We are still waiting for something to happen and pass a callback. We can say that the promises represent a value that is not available at the moment but will be available in the future. The syntax of promises makes the asynchronous code look synchronous. Let's see an example where we have a simple module that loads a Twitter feed. The example is as follows:

var TwitterFeed = require('TwitterFeed');
TwitterFeed.on('loaded', function(err, data) {
  if(err) {
      // ...
   } else {
      // ...
   }
});
TwitterFeed.getData();

We attached a listener for the loaded event and called the getData method, which connects to Twitter and fetches the information. The following code is what the same example will look like if the TwitterFeed class supports promises:

var TwitterFeed = require('TwitterFeed');
var promise = TwitterFeed.getData();
promise.then(function(data) {
  // ...
}, function(err) {
  // ...
});

The promise object represents our data. The first function, which is sent to the then method, is called when the promise object succeeds. Note that the callbacks are registered after calling the getData method. This means that we are not rigid to actual process of getting the data. We are not interested in when the action occurs. We only care when it finishes and what its result is. We can spot a few differences from the event-based implementation. They are as follows:

  • There is a separate function for error handling.
  • The getData method can be called before calling the then method. However, the same thing is not possible with events. We need to attach the listeners before running the logic. Otherwise, if our task is synchronous, the event may be dispatched before our listener attachment.
  • The promise method can only succeed or fail once, while one specific event may be fired multiple times and its handlers can be called multiple times.

The promises get really handy when we chain them. To elucidate this, we will use the same example and save the tweets to a database with the following code:

var TwitterFeed = require('TwitterFeed');
var Database = require('Database');
var promise = TwitterFeed.getData();
promise.then(function(data) {
  var promise = Database.save(data);
  return promise;
}).then(function() {
  // the data is saved
  // into the database
}).catch(function(err) {
  // ...
});

So, if our successful callback returns a new promise, we can use then for the second time. Also, we have the possibility to set only one error handler. The catch method at the end is fired if some of the promises are rejected.

There are four states of every promise, and we should mention them here because it's a terminology that is widely used. A promise could be in any of the following states:

  • Fulfilled: A promise is in the fulfilled state when the action related to the promise succeeds
  • Rejected: A promise is in the rejected state when the action related to the promise fails
  • Pending: A promise is in the pending state if it hasn't been fulfilled or rejected yet
  • Settled: A promise is in a settled state when it has been fulfilled or rejected

The asynchronous nature of JavaScript makes our coding really interesting. However, it could sometimes lead to a lot of problems. Here is a wrap up of the discussed ideas to deal with the issues:

  • Try to use more functions instead of closures
  • Avoid the pyramid-looking code by removing the closures and defining top-level functions
  • Use events
  • Use promises
You have been reading a chapter from
Node.js Blueprints
Published in: Jun 2014
Publisher:
ISBN-13: 9781783287338
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime