If you like to like to follow up with the coding part

  • install gitnode.js and npm 

  • git clone https://github.com/hrahal/jsconf_beirut

  • once you've cloned the repo, type: npm install  

Node.js async nature

A small intro

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. 

While most of the existing server side frameworks use a synchronous architecture, Node.js uses an asynchronous architecture that JavaScript can handle well.

 

This means that the server reacts to events and send events (messages) to e.g. the database. This style of programming is very different than the synchronous style.

so, what is synchronous anyway ?

demo

blocking code


/* 1/blocking.js */

var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());

console.log("Program Ended");

 

Most web applications are I/O Bound


meaning that 90% of a request’s processing time is spent on waiting for I/O to complete.

 

node.js solves that by relying on callbacks

/* 1/nonblocking.js */

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});

console.log("Program Ended");

non-blocking code using a

callback

/* 5/callback.js
 * */

function callmeMaybe(time, cb) {

    console.log("waiting for godot..");

    setTimeout(function() {
        cb(null, "godot is back! ");

    }, time);
};


callmeMaybe(5000, function (err, result) {
    console.log(result);
});

to understand Node.js more let's look at it's underlying architecture

this is a result of an interesting combination

node.js bindings

V8

JavaScript application

CommonJS - module loader

libuv

Google JS engine, that compiles JS to native machine lang, and manages garbage collection
Core JS modules for file system, network, http, crypto, events, etc..
CommonJS is responsible of loading modules and manage dependencies
Multi-platform library written in C that handles all async I/O operations.

this is what is also known as the

event loop

Node is also single-threaded

THREAD

WAITING

Traditional web server

Node.js web server

threads are costly, 

in a traditional W.S for 8 GB of ram you can get only 4000 concurrent connections, each 2 MB 

In node you can hold up to tens of thousands of requests at the same time. also use "cluster"
  

 

 

The restaurant analogy 

Synchronous:

 

|----A-----||-------B-----------||-------C------|

 

Asynchronous:

 

|----A-----|
   |-----B-----------|
       |-------C------|

I/O operations are expensive

I/O source CPU cycles
L1- cache 3
L2- cache  14
RAM 250
Disk 41,000,000
Network 240,000,000

misconceptions

// this example is **BROKEN**,
//we will fix it soon
var fs = require('fs');

function read(filePath, callback) {  
  fs.readFile(filePath, function(err, data) {  
    callback(data);
  });
}


Always check for errors in callbacks

// this example is **STILL BROKEN**..
// 6/callback.js
function read(filePath, callback) {  
  fs.readFile(filePath, function(err, data) {
    // here we check, if an error happened
    if (err) {
      // yep, pass the error to the callback
      // remember: error-first callbacks
      callback(err);
    }

    // no error, pass a null and the data
    callback(null, data);
  });
}
//this works fine

function read(filePath, callback) {  
  fs.readFile(filePath, function(err, data) {
    if (err) {
      return callback(err);
    }
    return callback(null, data);
  });
}
/*  4/writeloop.js
 *
 * async ops inside a for loop
 * */

var fs = require("fs"),
    i;

for (i = 0; i < 10; i += 1) {

    fs.writeFile("test" + i, "data" + i, function (err) {
        if (err) {
            console.log(err);
            return; //to insure the stop of exec
        }

        console.log("saved", i);
    });
}

visualizing it all


http://latentflip.com/loupe/

/* 3/maxsize.js
 *
 * */

function troll() {
    troll();
}

troll();
/* 2/block.js
 */

var fs = require('fs');
var fileName = 'input.txt';

var first_read = fs.readFileSync(fileName, 'utf8');

console.log("1st read >>" , first_read);

/* Create the test file (this is sync on purpose) */
fs.writeFileSync(fileName, 'updated text', 'utf8');

/* read the test file */
var last_read = fs.readFileSync(fileName, 'utf8');

console.log("last read >>", last_read);
/* 2/race.js*/
var fs = require('fs');
var fileName = 'input.txt';

/* read async */
fs.readFile(fileName, 'utf8', function(err, data) {
    if (err) {
        return console.log(err);
    }
    console.log("read 1 finished> ",  data || "EMPTY");
});

/* write async */
fs.writeFile(fileName, 'updated text', 'utf8', function(err) {
    if (err) {
        return console.log(err);
    }
    console.log("write finished ");
});

/* read async */
fs.readFile(fileName, 'utf8', function(err, data) {
    if (err) {
        return console.log(err);
    }
    console.log("read 2 finished> ",  data || "EMPTY");
});
console.log('done');
/* 2/race_solution.js */
var fs = require('fs');
var fileName = 'input.txt';

/* read async */
fs.readFile(fileName, 'utf8', function(err, data) {
    if (err) {
        return console.log(err);
    }
    console.log("read 1 finished> ",  data || "EMPTY");

    /* write async */
    fs.writeFile(fileName, 'updated text', 'utf8', function(err) {
        if (err) {
            return console.log(err);
        }
        console.log("write finished ");

        /* read async */
        fs.readFile(fileName, 'utf8', function(err, data) {
            if (err) {
                return console.log(err);
            }
            console.log("read 2 finished> ",  data || "EMPTY");
            console.log('done');
        });
    });
});

use case

register a user input from a file:

  1. Get  input
  2. check if the user is valid and has access
  3. check if catalog name exits
  4. check if categories exit in database
  5. check if features exits in the database
  6. save catalog

This coding style becomes a problem,

when your code grows in size

and nesting it  becomes annoying

callback hell


Control flow..

The need for

async.js

extra utils

Final Tip

Track your stack

understanding your stack trace, is a way to understand the code execution sequence 

// 3/stack.js  
  throw new Error("DAMN!");
    ^

Error: DAMN!
    at three (/home/hasan/JSConf/examples/3/stack.js:2:11)
    at two (/home/hasan/JSConf/examples/3/stack.js:6:5)
    at one (/home/hasan/JSConf/examples/3/stack.js:10:5)
    at Object.<anonymous> (/home/hasan/JSConf/examples/3/stack.js:13:1)
    at Module._compile (module.js:425:26)
    at Object.Module._extensions..js (module.js:432:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:457:10)
    at startup (node.js:136:18)

Thank You

nodeasync

By Hasan Rahal