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
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.
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);
});
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.
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"
Synchronous:
|----A-----||-------B-----------||-------C------|
Asynchronous:
|----A-----|
|-----B-----------|
|-------C------|
I/O source | CPU cycles |
---|---|
L1- cache | 3 |
L2- cache | 14 |
RAM | 250 |
Disk | 41,000,000 |
Network | 240,000,000 |
// 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);
});
}
// 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');
});
});
});
register a user input from a file:
This coding style becomes a problem,
when your code grows in size
and nesting it becomes annoying
callback hell
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)