with

Evan

Shortiss

What is Node.js?

  • Server-Side JavaScript Execution with V8
  • Provides High-Level API for I/O
  • I/O is Non-Blocking
  • I/O is Event Driven
  • Can be extended via C/C++

High-Level I/O?

var tcp = require('net');
var socket = tcp.createConnection(3000, '127.0.0.1');

// If there's no activity or failure to connect
// in ten seconds emit a timeout error and bail
socket.setTimeout(10*1000);

// Handle a timeout error
socket.on('timeout', function () {
  console.error('Socket timeout!')
});

// Handle the 'connect' event and 
// send a message, then close the socket
socket.on('connect', function () {
  socket.write('Hello!', 'utf8');

  // Send data and close the socket
  socket.end('Goodbye!', 'utf8');
});

Event-Driven?

var http = require('http');
var port = 8080;

var server = new http.Server();

// Event handling functions...

// Handle events
server.on('request', handleRequestFn);
server.on('connection', handleConnectionFn);
server.on('clientError', handleClientErrorFn);
server.on('close', handleCloseFn);
server.on('listening' handleListeningFn);
// ...etc

server.listen(port);

Non-Blocking?

var fs = require('fs');

// This is asynchronous, it's non-blocking! When it finishes 
// the provided callback is triggered with end results at a later point
fs.readFile('README.md', function(err, data) {
  if (err) {
    // Handle the error
    console.error(err)
  } else {
    console.log(data)
    // Do further logic
  }
});

console.log('This prints before the README.md is printed.');

// This is synchronous. It's blocking!
var data = fs.readFileSync('README.md', 'utf8'); // This might take a while...	
console.log(data);

console.log('This prints after the readFileSync finishes.');

How does it work?

The Node.js Event-Loop

Event-Loop

  • Stops code waiting on I/O
  • JavaScript is single threaded
  • Events are bubbled up to JavaScript for handling
  • JavaScript handles logic then moves on
  • I/O never blocked, separate to JavaScript

What's in the box (installer)?

Node Runtime

Runtime environment for Node.js code

$ node myscpript.js

Core Modules (Libraries)

  • Events (events)

  • TCP (net)

  • UDP (dgram)

  • HTTP (http)

  • HTTPS (https)

  • FileSystem (fs)

  • Utilities (util)

A package manager for Node.js

$ npm init
$ npm install request
$ npm install -g jshint
$ npm search jquery
$ npm uninstall request

(Over 100,000 modules available!)

Node.js vs Browser JS

  • No DOM in Node.js
  • Zero UI related concepts in Node.js
  • window is replaced with process (sort of)
  • No browser inconsistencies with Node!
  • Modularity and namespacing easy with Node

Modularity

The bread and butter of well written applications!

Unix Philosophy

  • Write programs that do one thing and do it well.

  • Write programs to work together.

  • Write programs to handle text streams, because that is a universal interface.

Node.js and Modular Code

  • Node.js follows Unix Philosophy and concepts
  • Many small highly focussed modules in Node.js
  • Node.js uses CommonJS to structure modules
  • Enables easily structuring large applicaitons
  • Each script file has very it's own scope
  • No more globals on window (hooray!)

package.json

Your project definition

package.json

Name

  • The name of your module
  • Appears on npm if published
  • Used as identifier when requiring the module

Main

  • The entry point for your module

Dependencies

  • Specifies dependencies required for deployment
  • Works using key and value entries
  • Keys are modules required
  • Value is the version of the module to install
  • Versions should never be set to *

Dependencies Command

$npm install {module-name} --save
$npm uninstall {module-name}

Dev Dependencies

  • Just like regular dependencies but for development
  • The same rules apply here as with dependencies
  • Testing frameworks and utilities can go here

DevDependencies Command

$npm install {module-name} --save-dev

Version

  • You know what this is for already
  • Node.js modules follow semver conventions

Scripts

  • Specify scripts associated with the project
  • Allows automation without a separate tool
  • Generally bash commands are placed here
  • Work as key value pairs
  • Key is the command name
  • Value is a string representing the bash command

Scripts Example

"scripts" {
    "test": "mocha ./test -R spec"
    "format": "jshint ./src/*.js"
}
$  npm run-script format
$  npm run-script test

Working with Modules

It's easy!

Core Module Concepts

  • Think of each .js file as a module (a JavaScript closure)
  • All functions and variables within are private
  • Make items public via the exports object
  • Gain access to a module using require
  • Read the module docs, you'll be glad you did

Think #include or #import

Think of the public keyword

Math Example

/**
 * @filename: math.js
 */

// This is private
function inverse (num) {
  return num * -1;
}

// A public function!
exports.subt = function (a, b) {
    return a - b;
};

// Another public function with a local reference
var add = exports.add = function (a, b) {
    return a + b;
};

exports.inverseOfAddition = function (a, b) {
  return inverse(add(a, b));
};

Math Example Usage

/**
 * @filename: main.js
 */

var myMath = require('./math.js');

var a = 10;
var b = 34.2;

myMath.add(a, b); // ==> 44.2
myMath.subt(a, b); // ==> -24.2
myMath.inverseOfAddition(a, b); // ==> -44.2

What about classes?

Sharing a Class Module

/**
 * @filename: User.js
 */

function User (name, age) {
  this.name = name;
}
// Set the exports object to our class
module.exports = User;

User.prototype.getName = function () {
  return this.name;
};

// ===================================

/**
 * @filename: main.js
 */

var User = require('./User');
var u = new User('Evan');

What about NPM?

NPM Modules 

  • Define required modules in package.json
  • Installed in the project folder in node_modules
  • Global modules can be installed via -g flag
  • require using the module name, no path required
  • Never use * for module version in package.json

Best Practices

"With great power comes great responsibility"

Asynchronous Operations

  • Always have a callback as the last parameter...
  • ...or return an object that can have Events bound
  • Callbacks should take two params Error & Result
  • Error is Null if no errors were encountered

Sample Callback Function


// The callback we'll use
function callMeOnceComplete (err, res) {
  if (err) {
    console.error(err);
  } else {
    console.log(res);
  }
}


// An async function that provides results via callback
callSomeAsyncOperation(callMeOnceComplete);

Asynchronous Function

var fs = require('fs')

// Asynchronously reads a JSON file and parses it
function readJsonFile (filepath, callback) {
  fs.readFile(filepath, function (err, data) {
    if (err) {
      return callback(err, null);
    }

    try {
      callback(null, JSON.parse(data);
    } catch (e) {
      callback(e, null);
    }
  });
}

Callback Hell

  • Occurs when code is not modular
  • Makes reading and debugging difficult
  • Can be avoided with code structure
  • NPM modules such as async help

Callback Hell


// Avoid this situation! It's untidy.

firstAsync(data, function (res1) {
  secondAsync(res1, function (res2) {
    thirdAsync(res2, function (res3) {
      fourthAsync(res3, function (finalResult) {
        // Do something with the result
      });
    });
  });
});

Callback Hell Resolved!


var async = require('async');

// Run provided functions in sequence
async.series([
    firstAsync,
    secondAsync,
    thirdAsync,
    fourthAsync
], function (err, res) {
    // Called when all other functions are complete
});

Control Flow

  • Manage order of execution
  • Prevent too many concurrent operations
  • Iterate asynchronously in a controlled manner
  • Prevent too many open sockets/file descriptors

No Control Flow


// Don't ty this at home folks!

var fs = require('fs')

function saveUserFiles (fileList)
  // This will run all the writes in parallel...
  // ... what will happen if we have 5000 files to write!?
  fileList.forEach(function (data) {

    fs.writeFile(data, function (err) {
      // This will be called for each item! 
      // This is an anti-pattern!
      if (err) {
        console.error(err);
      } else {
        console.log('Saved %j', data);
      }
    });

  });
}

With Control Flow

var async = require('async');
var fs = require('fs');

var MAX_CONCURRENCY = 10;

function writeToFile (listItem, cb) {
  fs.writeFile(listItem.data, cb);
}

function saveUserFiles (fileList, callback) {
  // Write up to MAX_CONCURRENCY files at a time
  // Each file is passed to the writeFile function
  // When all writes are complete the callback is called
  async.eachLimit(fileList, MAX_CONCURRENCY, writeToFile, callback);
}

Recommended Material 

Node.js Overview

By Evan Shortiss

Node.js Overview

A basic overview of Node.js core concepts.

  • 1,195