JavaScript and asynchronous code - Node.js for .NET Developers (2015)

Node.js for .NET Developers (2015)

Chapter 2. JavaScript and asynchronous code

If you go to the actual NodeJS.org home page and not just the download page, you might not need to scroll to find a code sample that looks pretty much like this:

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1234, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1234/');

This sample is followed by a section that says the following:

"To run the server, put the code into a file example.js and execute it with the node program
from the command line:"
% node example.js

That produces output in the console that says this:

Server running at http://127.0.0.1:1234/

Feel free to try it from the correct file-location command prompt. (In this case, it will work both from where you did the original Node.js install and also where you set up your Microsoft Visual Studio path to run your Node.js project.) From there, you will probably get the output promised.

If you do, you can then launch a browser to the IP you specified. (The port you originally specify is totally up to you as long as you use four digits.) You will see your “Hello World” output on the page. Although you aren’t going to be using this code, you’ll do something very similar. Just these few lines of code serve to illustrate a number of things you need to know about JavaScript and also the way that Node.js expects you to use it—not to mention it also works if you really did create a server with six lines of JavaScript. This is just a smidge easier than installing Internet Information Services (IIS)! (Of course, this server isn’t doing much yet, but it still isn’t too bad.)

Working with JavaScript

I won’t assume you have lots of JavaScript knowledge here, but I will assume you have at least some experience with it. I’ll start from the beginning, but I will move quickly. If you need to do some of your own investigation to explore in a deeper way ideas and concepts I will brush across, you’ll find many resources available, including whole books, websites, and discussion groups dedicated to nothing but the practice of coding JavaScript.


Image Note

In the same way, if you do know the subject, feel free to skim over this chapter or large sections of it. I am not preaching about a particular way to do JavaScript. I am simply showing how I do it, which seems to work to address the real-world needs I have on a daily basis as a commercial software developer.


Let’s take a look at the code and see what you can learn from it, starting with line 1:

var http = require('http');

Or you can start with the first word:

var

This is a variable type declaration. Say goodbye to strong typing! Or, for that matter, say goodbye to identifying what something is by looking at it.

This is the first idea you have to wrap your head around in JavaScript. Nothing knows anything about types until run time, and even then it’s dodgy. This means, for example, you can have this:

var iNumber = 0;
var sNumber = "20";

Put these together using the plus (+) sign like this:

var iResult = iNumber + sNumber;

You will get no type mismatch errors, and you will not get addition even if you hoped for it. You will get concatenation:

020

Now suppose you are used to programming in Microsoft Visual Basic.NET and you accidentally concatenate using the ampersand (&) symbol in one place, like this:

var iResult = iNumber + sNumber & myNewNumber;

Now you get this as output:

0

What’s happening? In JavaScript, the ampersand is a bitwise operator that returns true or false. In this case, the output 0 means false and has exactly nothing to do with your variable values in code. Additionally, JavaScript is not strict with declarations and would actually let you try to run exactly the code just shown as is, not warning you that myNewNumber didn’t exist until runtime when the code suddenly and unexpectedly failed.


Image Note

The newest browsers will now recognize the use strict statement. If you place this globally in your .js file, it will at least prevent the last instance just shown.


Admittedly, this is just sloppy coder coding, but it illustrates how the general idea of a non-typed language can lead to a whole mess of problems if you aren’t extremely careful. I have always been a big advocate for a limited “Hungarian notation” when coding, meaning you declare an int with the name iValue and a string with the name sValue so that when you have 3,000 lines of code, you know what’s what when debugging without having to dig back through your code and find the type declaration, wherever it might be.

JavaScript is a keep-it-neat idea on steroids, so it won’t help you with types at all. If you don’t tell yourself, by variable name, what this value or object is that you have running around in your code, you are making a heap of trouble for yourself.

Keep in mind that this problem soon cascades into your functions, as illustrated by this part of line 2 of the earlier code:

function (req, res)

This is a function declaration in JavaScript that takes two passed in arguments. As you can see, they have no types at all. In this case, JavaScript has to look at the rest of the call in which this function is embedded (which I will discuss in more detail shortly) and match it to an expected signature in the npm package. The npm package is just a JavaScript file library that, no doubt, contains some JavaScript object, the type of which is expected in this function as argument one or two. Thus, if you make a reference to some property of the object, such as on line 4 shown here, somewhere up the tree it fits together:

res.end(...);

This isn’t exactly intuitive if you come from the land of strong typing! You simply have to know the right properties to use and how to use them. You’ll get a lot of very unintuitive errors about things being “undefined” when you don’t.

I didn’t mean to jump around the code shown earlier, but you can see everywhere how important this idea of non-strong-typing really is within JavaScript. Danger! Danger everywhere, Will Robinson! So be advised.

Now that I’ve covered that, let’s get back into the code shown earlier and the line where you left off:

var http = require('http');

This is the standard way of including a Node.js module in your code. Think of it as your import or includes statement. If you don’t have the module installed that you try to reference in this line, your code will throw an error telling you it has no clue what you mean.

Here you didn’t actually install an “http” module, so the need to do this process right here is just a quirk of this particular Node.js base library. And, as I said, you won’t be using it anyway. But the require function call is the same in almost all cases, and it returns some object that is useful in some important way. Again, you are the one who needs to know what the object and its properties will be when it returns to you.

In this case, http has just one important thing that it can do, and that is create a web server using the http.createServer(); function call. That function call contains another boatload of JavaScript information and, equally as important, information about how Node.js expects you to write it for everything to work properly. The line of code without the meat in the middle is http.createServer(function (req, res) {});.

Yes, this really is a function that takes a function as an argument. In JavaScript, even a function is an object. The closest comparison in .NET-land would be a delegate, which is an object wrapper around a function.


Image Note

I actually said that backwards. The fact is every object in JavaScript is declared as a function. I’ll get to this, too, very shortly. I do realize that if all you have done with JavaScript up until this point is hide and show some divs with JQuery, this is fairly mind-expanding stuff. I’ll keep it as simple as I can and let you run with the rest when you’re ready.


Let’s get back to our one line of wondercode:

http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1234, '127.0.0.1');

The special thing about JavaScript and passing functions to functions (such as function A as an argument to function B) is that function B doesn’t actually use that function per se or anything that it does. Instead, it wires that function as a callback.

A callback function is basically the same as a callback from your local phone company. If you call the phone company (if it’s a modern phone company, that is) when customer service is busy, you will get an option to leave your number and have the company return the call or “call you back” when they are ready to service you. In other words, they’ll call you when they are done doing whatever else they were doing before they could get to you. The callback function A receives the result of function B and then can act upon that result.

This concept, using callback functions in every action you take in JavaScript, is the essence of Node.js programming. It creates what is known as the asynchronous model of code.

Something is synchronous if it always runs in order, A and then B and then C. But if B is a callback function, meaning it begins to run only when function A has some final result for it to start working on, then C could run after A finishes but before B is done running, making the code asynchronous:

obj.myFunctionA(functionB (x) {});
obj.myFunctionC();

Just passing an argument that JavaScript thinks is an object like any other (!) doesn’t stop code from running. So the JavaScript processor (which could be the subject of a whole book on its own if it isn’t already) thinks that function A is done when whatever is in its brackets is done running. It couldn’t care less about the fact that function B hasn’t yet finished doing what it needs to do before function C goes ahead and leaves the starting gate.

Despite whatever complications this idea introduces to your code and data logic, it contains a tremendous amount of power and potential. At its most basic level, if function A is a web request and function C is also a web request, then C does not have to wait for the results of A (meaning B) to finish processing or rendering before beginning to run. As long as B, which is now running in its own little universe, is not touching any resources common to C, by following this model cleverly enough you can conceivably craft a way to spin a huge number of concurrently running universes, all processing their own requests in their own space. They are completely independent of any other resource and serve up webpages at the absolute optimum speed for each one.

All of this helps explain some of the anecdotal evidence I referred to at the beginning of the book, and it explains why we have Node.js in the first place. The widespread use of the callback model creates asynchronous processing of web requests, and it can have a huge positive impact on performance.

That’s really all it is. After that, you do everything by hand to fit the model. Doing this, of course, would be practically impossible on a day-to-day commercial basis when compared dollar-for-dollar against almost anything with prebuilt toolkits containing everything from controls to styling to database connections and more. Hence, you see an explosion in the number of npm packages to bring those development times more in line with other approaches and, thus, the current state of Node.js development.

Once you accept all the concepts I’ve touched on in the last few pages, you can start to dissect what’s happening:

http.createServer(
function (req, res)
{
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
})

The function createServer is not taking any arguments other than a callback function. So somewhere in the Node.js backbone lives this HTTP object. It does what it does, expecting you to provide a function with exactly this signature, which it will then call when it is done. That callback function is expecting to pass two arguments generated by the backbone. As the names imply, these arguments will be objects you are familiar with in the world of HTTP: a request object and a response object.


Image Note

As I talked about earlier, in the land of JavaScript there is no way to know what objects are without cluing yourself in by using the name and then referring to your own code or whatever documentation you can get your hands on. These are some of the reasons this book is intended for people who have had at least some experience doing real-world web development. For a novice programmer, Node.js would be totally baffling without it.


You can see that the response object contains properties and functions you can recognize: a writeHead function and an end function that has at least one overload version that accepts some plain text to write to the response as it ends itself. In this case, we are writing Hello World.

One major thing to note: as written, this server will return Hello World for every web request it receives. That’s not very practical. How to recognize routes and then do the routing is covered later. You will use the features contained in the Express npm package as the backbone for that functionality, not the Node.js core itself. But you can dig down into the HTTP object and this callback function, and you can do it here if you choose to.

Finally, you need to turn it on. That’s what this line does:

.listen(1234, '127.0.0.1');

Whatever object is returned from the createServer function you called must have a listen function as one of its properties that activates the underlying server code. And that function needs an IP address and a port on which to listen for requests. The code could be written as follows for clarity:

var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');});
server.listen(1234, '127.0.0.1');

However, the infamous JavaScript processor apparently prefers the “chained” version for reasons of its own. So use it when you can.


Image Note

Optimized JavaScript can lead to some of the most unreadable code on the planet. It is simply a tradeoff you are required to make for the sake of performance if that level of performance really does matter to you—and it might, depending on the circumstances.


How’s that for one line of code? Welcome to the heart and soul of JavaScript Land—its capital city, Node.js.

The infrastructure supporting the capital city, referred to no less than four times in the one line of code just shown, is Object-Oriented JavaScript (OOJS). Some objects in the preceding code are created; others are referenced by inference, and still others are referenced directly using properties or methods. Anything as important as the underlying foundation for a kingdom better be solidly designed, so without getting overly verbose on the subject let’s spend a few minutes talking about some best practices for OOJS:

Object-Oriented JavaScript

As I mentioned earlier, every object in JavaScript is declared as a function:

function Person(){}

In turn, as you saw from the entire callback structure, every function also acts as an object. Some people in JavaScript Land use Function to refer to an object (a collection of related code) and function to refer to a method (code that performs a single specific action). I’ll stick with the standard references, all similarities in syntax aside.

Let’s begin with an object you will actually build on and use later as you go along. You’re going to design a simple Sports Survey Web Application that will generate a few questions to a UI and allow users to interact and generate responses to those questions. There are a couple of what I refer to as classes (objects) that you will need to offer—for example, a few choices between players on a question. Those objects, to start with, include a Player object and a Question object.

A Player is a Person, so let’s start with the line of code shown earlier and something common to all people and players:

function Person(){
this.lastName = "";
this.firstName = "";
}

If you are wondering when you declared that property or the fields/properties called lastName and firstName, the answer is “You just did.” The magic keyword this has created the reference to those properties on the fly. Yes, this has innumerable potential hazards. No, this is not unique to Node.js. So let’s just plow on as if we didn’t have any issues at all. Life in JavaScript Land is like this every day. You get used to it by being extremely careful in your code practices.

So next you narrow down the kind of person you need, and that is a player. First and foremost, a player is a person. When you create the Player object, first you tell it that it is a Person by using the prototype property like this:

function Player () {
this.prototype = Person;
this.sport = "Football";
this.displayName = function(){
return(this.lastName + ',' + this.firstName);
}
}

As you see, to make a property into a method, you just declare it as a function. Now any Player object you declare will also have all the properties of a Person, and this code will work to pop up a message box with the concatenated name of the player:

var oPlayer = new Player();
oPlayer.firstName = "Walter";
oPlayer.lastName = "Payton";
alert(oPlayer.displayName());

In this case, it seems as if you should actually call this object FootballPlayer. As you can see from the code, the value Football is hard-coded into the sport property, thus defining all Players who are instantiated using the Player object function call (constructor) as FootballPlayers. Play with this pattern as you like using prototypes to help encapsulate your code and eliminate redundancy where you can.

You can override a function in the prototype from the child class this way:

this.displayName = function(){
this.lastName + ',' + this.firstName;
}
}

This is just like any other property. You simply set the value to something new—in this case, a new function. So you could have made the code look like this for a BaseballPlayer object:

var oPlayer = new Player();

oPlayer.firstName = "Babe";
oPlayer.lastName = "Ruth";
oPlayer.position = "Pitcher"; // yes he started as a pitcher!
this.displayName = function(){
this.lastName + ',' + this.firstName + ":" + this.position;
}
alert(oPlayer.displayName());

It produces similar output as before, only from slightly neater code.

Again, you can add new properties and functions willy-nilly (either intentionally or by accident) to your child class. You are not required to implement anything at any level of the so-called object tree as with genuine inheritance for .NET objects and interfaces. These kinds of OOJS practices are recommended only for code consistency, maintainability, and patterning for your benefit. That means if you prefer to call your Player function showAllOfMe, feel free to go ahead. JavaScript will not care at all.

var oPlayer = new Player();
oPlayer.firstName = "Walter";
oPlayer.lastName = "Payton";
this.showAllOfMe = function(){
this.lastName + ',' + this.firstName + ":" + this.sport;
}
alert(oPlayer.showAllOfMe());

However, if you’re going to override every function of your prototype base class, there is little point in having a child/prototype set up because all it does is add convenience.

Now you have the Player object that will help you assemble answers to your sports survey questions. You will need a list of questions, thus the next object you need is a Question object:

function Question(oQuestion, oAllAnswers, oCorrectAnswer) {
this.questionText = oQuestion;
this.answersList = oAllAnswers;
this.correctAnswer = oCorrectAnswer;
}

As you can see, this function is expecting a bit more to make a Question object than the Player function did to make a Player object. This object clearly needs some outside data to assemble properly. Again, notice the weak typing. For example, the following is obviously going to be some kind of list or array. That’s obvious only because its name implies that it must be such a list to work as you logically need it to:

this.answers = oAllAnswers;

The following line is equally obviously only a single value holding, most likely, just text. (There is only one possible bit of text content for any individual question.)

this.questionText = oQuestion;

And this next line also clearly will hold a single value of whatever kind you choose that works best for you. However, just based on this, you don’t know if it’s a numeric or GUID-style ID that matches some AnswerID you have somewhere or if it’s a text value that matches the actual content of a displayed answer.

this.correctAnswer = oCorrectAnswer;

Try to develop good habits when writing code so that you can tell what is going on in your code at all times. It will make your life much easier. I’ll get back to fleshing out our “classes” later as you develop the code.

As I said earlier, my intention here is only to scratch the surface of OOJS to introduce you to its best practices for consistency and maintainability. The details of the various designs for prototyping and deriving objects of all kinds can be explored in much greater depth from a wide range of resources if you have the need and interest. For your purposes at this point, the basics of OOJS and how it is used in existing packages are enough to wrap your head around. These basics also cover how you should use OOJS to pass and manage information and behavior in your Node.js project.

You have seen the Node.js core create a server from a single, albeit slightly complex, function call. You poked around into OOJS to get a feeling for good patterns and functionality within the JavaScript world. And you combined those to analyze that single line of JavaScript code to help you better understand what that code actually does. In addition, examining that code showed you how best to use Node.js to follow the most effective patterns when building a site.

I’m sure you’re more than ready to start creating something that works—an actual website generated from Node.js. You have all the background you need to do so effectively. So let’s begin right now.