Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Node.js

NODEJS Overview

What is Nodejs?

  • Platform built on Chrome's JavaScript runtime, for building fast, scalable network applications
  • Nodejs uses event driven, non-blocking I/O

NOTE

V8

  • virtual machine, runtime engine of Chrome
  • Nodejs uses V8 for server-side programming
  • V8 boosts performance of Nodejs apps since it directly compiles Nodejs code into machine code rather than using an interpreter

Nodejs Philosophy

Nodejs follows pragmatic Unix principals:

  • Modularity: keep parts short and simple
  • Singularity: A program should do one thing, and it should do that very well

This pragmaticism in Nodejs is provided by 'modules'. A Module provides:

  • resusability of code
  • easier to understand code
  • easier to test and maintain

Benefits of Nodejs using JavaScript?

  • web apps can be built in one language
    • it reduces context switching between client and server development
    • it allows code sharing between client and server
  • JSON is popular data interchange format & native to JS
  • JS is used by most NoSQL databases: MongoDB, CouchDB
  • Nodejs uses V8 that is up to date with ECMAscript standard
    • developers don't have to wait for all browsers to adapt new JS lang features in Nodejs

REPL

  • stands for READ EVAL PRINT LOOP
  • Nodejs Shell
  • process:
    • reads input from user
    • evaluates input into JS Code
    • prints result
  • useful for experimenting small snippets of code
  • o start Node Shell: $ node
  • ">" is the REPL prompt
$ node
> var foo = "Hello World";
undefined
> console.log(foo);
Hello World
undefined
  • Why undefined?
  • because the variable declaration statement returns no value
  • Three dots(...)
> console.log(
... "Bye"
... );
Bye
undefined
  • indicates multiline expression
  • increase in the level of dots means increasing level of nesting

REPL Command Line

  • .help
  • .exit
    • another way to exit Node Shell is Ctrl+C twice
    • Ctrl+D once to exit the Node Shell
  • .save filename
    • saves current REPL session in filename
  • .load filename
    • executes JS file
  • .clear
    • resets REPL shell
  • -e option
    • allows js code to run inline
$ node -e "console.log('Hi');"
Hi

[More info on Command Line and Special Keys](https://nodejs.org/dist/latest-v6.x/docs/api/repl.html#repl_commands_and_special_keys

Building HTTP Server with Nodejs

  • a very common use case for Nodejs is to build Servers
  • in Nodejs, server and the application are the same
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () =>{ 
  console.log(`Server running at http://${hostname}:${port}/`);
});

Difference between Blocking and Non-Blocking Calls in Nodejs

  • Blobking methods execute synchronously and non-blocking methods execute asynhcronously
  • execution of additional JS in Nodejs process must wait until the execution of non-JS operation completes (blocking operation)
  • this happens because event loop is unable to run JS code until blocking operation completes
  • Most commonly used synchronous methods are from Nodejs standard library
  • All I/O methods in Nodejs standard library have asynchronous versions which are non-blocking and and accept callback functions

Example of Synchronous file read:

const fs = require('fs');
const data = fs.readFileSync('/file.md'); // blocks here until file is read
console.log(data);
// moreWork(); will run after console.log

Example of Asynchronous file read:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
  console.log(data);
});
// moreWork(); will run before console.log

Concurrency

JavaScript execution in Node.js is single threaded, so concurrency refers to the event loop's capacity to execute JavaScript callback functions after completing other work. Any code that is expected to run in a concurrent manner must allow the event loop to continue running as non-JavaScript operations, like I/O, are occurring.

Node Programming Model

Published on Medium Node.js System

Nodejs follows event driven Programming

  • Event-driven programming is a programming paradigm in which the flow of the program is determined by events such as user actions (mouse clicks, key presses), sensor outputs, or messages from other programs/threads.
  • In practice, it means that applications act on events.

Nodejs Thread Model

  • Nodejs is single threaded
  • Event Loop provides Nodejs the event driven Programming
  • Event Loop schedule tasks(/events)
  • Each time an event occurs, it is placed in Node's event queue
  • On each iteration of event loop, a single event is dequeued and processed
  • If during the process, the event creates any additional events, they are simply added to the end of the queue along with a callback
  • NodeJS uses callbacks to avoid waiting for blocking I/O.
  • When the event is executed, control is returned to the event loop and another event is processed.
  • When all events on NodeJS event queue are executed, NodeJS terminates the application.

Nodejs System

NodeJS System

Building blocks of NodeJS:

  • Reactor Pattern
  • libuv
  • Set of Bindings
  • V8
  • Core JS Library

I/O is Slow

  • I/O is the fundamental operation of a computer
  • it is slow
  • Accessing the RAM is matter of nanoseconds
  • While accessing the data on disk or network is a matter starts in milliseconds

Blocking I/O

  • Traditionally, a system calls an I/O request that comes to a webserver and is assigned to an available thread
  • for each concurrent connection there is a thread
  • The request is handled continuously on that thread until the request is complete and response is sent. Tradtional Thread Model
  • while handling the data between Functions such as GetFile(file) & Open(File) there will be some amount of idle time
  • thread consumes memory, so long running thread for each connection & not using it is not efficient.

Traditional Non-Blocking I/O

  • system call is returned immediately without waiting for the data to be read or written (aka operation to complete)
  • If no results are available at the moment of the call, the function will simply return a predefined constant, indicating that there is no data available to return at that moment.
  • A loop iterate over the resource and when resource is found (operation is completed) it is returned.
  • This loop consumes CPU for iterating over the resource that are unavailable most of the time.
  • This is called busy-waiting. Traditional Non-Blocking I/O.

Event Demultiplexing

  • modern way of Non-Blocking I/O
  • for efficient Non-Blocking I/O there is synchronous event-demultiplexer (aka:event notification interface)
  • It collects and queues I/O events that come from requests and block(sits idly ) until new events are available to process.
  • Each event is associated with a specific operation
  • Event notifier is set on the group of resources to be watched
    • This call is synchronous and blocks(idle time) until any of the watched resources is ready for a read
    • Event Demultiplexer returns from watched resources when they are being processed and a new set of events are available to be processed.
  • Each event returned by the event demultiplexer is processed.
  • When all the events are processed, the flow will block again(there will be idle time in between) on the event demultiplexer until new events are again available to be processed. This is called the event loop.
  • This way several I/O operations can be handled in a single thread.
  • Advantages:
    • minimalize idle time
    • having a single thread handle multiple requests

Reactor Pattern

  • have a handler(in case of Nodejs, have a callback function) associated with each I/O operation
  • this handler/callback function is invoked when event is produced & processed by Event Loop.

What happens in an Application using the Reactor Pattern?

  • application generates I/O request by submitting request to Event Demultiplexer
    • The application also specifies handler/callback function which will be invoked when operation completes
  • Submitting a new request to the Event Demultiplexer is a non-blocking call and it immediately returns the control back to the application.
  • When I/O operation completes, the Event Demultiplexer pushes the new events into the Event Queue.
  • At this point, the Event Loop iterates over the items of the Event Queue.
  • For each event, the associated callback function is invoked.
  • callback function will give control to the event loop when its execution completes.
  • New asynchronous operations might be requested during the execution of the callback function, causing new operations to be inserted in the Event Demultiplexer
  • When all items in Event Queue are processed, and there no pending operations in Event Demultiplexer, Node.js application will exit automatically.

libuv - Non-Blocking I/O Engine

  • pronounced as "lib u v"
  • Each operating system has its own interface for the Event Demultiplexer
    • eg: epoll on Linux, kqueue on Mac OS X & IOCP on Windows
  • each I/O operation can behave quite differently depending on the type of the request, even within the same OS.
    • this creates inconsistency
    • to overcome this inconsistency, Nodejs Core team built libuv
  • libuv is a C library, created to make Node.js compatible with every OS & normalize the non-blocking behavior of the different OS types.
  • libuv is a low-level I/O Engine
  • it implements Reactor Pattern, providing an API for creating even loop, managing event queue, running asynchronous I/O operation.

Set of Bindings

  • responsible for wrapping and exposing libuv and other low-level functionality to JavaScript.

V8

  • JavaScript runtime engine
  • compiles and executes JS
  • developed by Google for Chrome browser
  • reason why Nodejs is fast
  • V8 is acclaimed for its revolutionary design, its speed, and for its efficient memory management.

Core JS library

A core JavaScript library (called node-core) that implements the high-level Node.js API.

Complete NodeJS System


Asynchronous Programming

  • referred as Continous Passing Style (CPS) programming
  • async functions take an extra argument
    • a function that is called after the async code has finished executing
    • this extra argument is referred as continuation or callback function
  • Important Convention: if a function takes a callback function as an argument, it should be the final argument. Also, if the same function takes an error as an argument, that should be the first argument

Callback?

  • function that is executed asynchronously (or at a later time in the event loop)
  • asynchronous programs execute different functions at different time based function's speed and order. (Eg: a file read from system will take more time)

An Example of Blocking I/O, Non-Blocking I/O & Callback

Consider that you are visiting a fast food joint where you order a burger at cash counter. The cash counter will take your order and will make you wait for your order to complete. In the mean time, the cash counter can take other orders and start cooking them for other people too.

If you are made to wait at the cash counter for your order, blocking all other customers in line from ordering while the fast food joint completes your order. That's BLOCKING I/O since completing orders happen one at a time. There will be some amount of idle time while the food is being cooked.

Nodejs in NON-BLOCKING, meaning it can take as many orders and complete them based on the time taken to cook a particular order.

Now, imagine that the fast food joint has given you number on your table while you wait for your meal. That's a callback (the number).


Callback Hell

  • occurrs when callbacks are nested within callbacks
  • leads to difficutly in maintaining and confusion
  • referred as Pyramid of Doom

Example:

fs.exists(fileName, function(exists) {
  if (exists) {
    fs.stat(fileName, function(error, stats) {
      if (error) {
        throw error;
      }
      if (stats.isFile()) {
        fs.readFile(fileName, "utf8", function(error, data) {
          if (error) {
            throw error;
          }
          console.log(data);
        });
      }
    });
  }
});

Avoiding Callback Hell

  • one way is to use named functions instead of anonymous functions

Example:

function cbReadFile(error, data) {
  if (error) {
    throw error;
  }
  console.log(data);
}
function cbStat(error, stats) {
  if (error) {
    throw error;
  }
  if (stats.isFile()) {
    fs.readFile(fileName, "utf8", cbReadFile);
  }
}
function cbExists(exists) {
  if (exists) {
    fs.stat(fileName, cbStat);
  }
}
fs.exists(fileName, cbExists);

Node Module System

  • In Node.js, files and modules are in one-to-one correspondence (each file is treated as a separate module)

require()

  • to import modules into nodejs programs (importing aka module loading)
  • accepts single argument as string:
    • path to the module
      • if path exists, returns an object to interact with module
  • require is one of the few synchronous I/O operations available in Node. Because modules are used often and are typically included at the top of a file, having require be synchronous helps keep code clean, ordered, and readable.

Core Modules

  • modules compiled in Node binary
  • highest precedence given by require()
    • meaning if there are two modules of the same name and one of them is core module, require will load core module

File Modules

  • non core modules
  • loaded from the file system
  • module path begins with (.) or (..) as relative paths
require('./some-module');
require('./path/to/module');
require('../another-module');

3rd Party Modules (npm Modules)

  • If the module path does not correspond to core module or filesystem module, Node begins searching for the module in node_modules directory (such as in case of third party modules)

Module Caching

  • Modules are cached after the first time they are loaded
    • meaning every call to require('foo') will return the same object
    • multiple calls to require('foo') may not cause the module to execute mutliple times

module Object

  • module represents the current module
  • module.exports defines what a module exposes that is available through require
  • exports is a property of module Object, act as a reference to module.exports

Exporting Node Modules

allows developer to select what functions from the included files are exposed to the main application:

  • use module.exports = function functionName(){} if module is returning a single function
  • use exports.functionName = function(){} when module is returning more than one function
  • if you create a module that populates both exports and module.exports, module.exports will be returned and exports will be ignored.

Everything is local in Nodejs Module

  • By default, JS puts everything into global scope. Node.js is designed to behave differently, with being local by default
  • Top-level scope in Node.js is not global scope. var a inside a Node.js module is local to that module

Node.js Event Loop

  • allows Nodejs to perform non-blocking I/O operations
    • by offloading operations to system kernel
      • modern system kernels are multithreaded so they can handle multiple operations
      • when an operation completes, kernel tells Nodejs by adding appropriate callback to poll queue to be eventually be executed

The following diagram shows a simplified overview of the event loop's order of operations:

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

Additonal Resource:

NGINX

  • HTTP server, like APACHE
  • uses event loop with asynchronous I/O (just like browser and Node)
    • this helps in handling more requests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment