If you like to like to follow up with the coding part
-
install git, node.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:
- Get input
- check if the user is valid and has access
- check if catalog name exits
- check if categories exit in database
- check if features exits in the database
- 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)