The only thing I have done before watching the codeschool course was to download and run the installation file
from https://nodejs.org.
It installs node
and npm
(the package manager for javascript).
You don't actually need to, but it's always nice to see something actually working.
Main points:
- non-blocking
- event loop - checking for events continuously (e.g. request, connection, close)
Here is a non-blocking code example:
fs.readFile('/etc/hosts', function(err,contents) {
console.log(contents);
});
console.log('Doing something else');
or equivalently
var callback = function(err,contents) {
console.log(contents);
});
fs.readFile('/etc/hosts',callback);
The Hello World, which you can run with
node hello.js
var http = require('http')
http.createServer(function(request,response) {
response.writeHead(200, {
'Content-Type': 'text/html'
});
response.write("Hello!");
setTimeout(function(){
response.write("I am done.");
response.end()
}, 5000);
}).listen(8080);
Events
Many objects in node emit events, for example, fs.readStream
emits a data
event,
and obviously we can listen for these events. The objects that can emit events inherit from
the EventEmitter class.
We can create custom EventEmitter:
var EventEmitter = require('events').EventEmitter;
var logger = new EventEmitter();
If you want to listen for error events:
logger.on('error',function(message){
console.log('Error: ' + message);
});
And to trigger the event:
logger.emit('error','An error');
In fact, earlier, when we called http.createServer(function(request,response) {...})
,
this returns a http.Server
object, which is an EventEmitter,
set to listen for request
events (it is all in the documentation for node.js).
It is possible to write it this way:
var server = http.createServer();
server.on('request', function(request,response}{
response.writeHead(200);
response.write("Hello, this is dog");
response.end();
});
which is how you can explicitly bind several listeners on the same object.
Actually, you can listen to an event more than once:
var http = require('http');
var server = http.createServer();
server.on('request', function(request, response) {
response.writeHead(200);
response.write("Hello, this is dog");
response.end();
});
server.on('request', function(request, response) {
console.log("New request coming in...");
});
server.on('close', function(){
console.log("Closing down the server...");
});
server.listen(8080);
In the code above, whenever a request event happens, two things happen: send a response and write to console.
Streams
Streams is about how data is transferred back and forth. Streams can be readable, writable, or both. For example,
in the arguments for http.createServer
, request
is a readable stream, response
is a writable stream. These streams are kept open until we close the connection.
How do you read from a stream (e.g. how do you read request
?). The request
object is an EventEmitter,
and it emits two events: 'readable', when the server can start reading the data, and 'end', when the client finishes uploading
the data. Here is an example code:
http.createServer(function(request,response){
response.writeHead(200)
request.on('readable',function(){
var chunk = null;
while (null !== (chunk = request.read())) {
response.write(chunk);
}
});
request.on('end'), function() {
response.end();
});
}).listen(8080);
(the .write function implicitly does toString()). However, there is an even faster method, using pipe:
http.createServer(function(request,response) {
response.writeHead(200);
request.pipe(response);
}).listen(8080);
How is that useful? Well, the second main example is about reading and writing file:
var fs = require('fs'); // require the filesystem module
var file = fs.createReadStream("readme.md");
var newfile = fs.createWriteStream("readme_copy.md");
file.pipe(newFile);
You can for example do
request.pipe(newfile)
, and when you
for example do
$ curl --upload-file readme.md localhost:8080
it will upload readme.md to newfile. Here is a complete implementation of file upload with progress update:
http.createServer(function(request,response){
var newFile = fs.createWriteStream("readme_copy.md");
var fileBytes = request.headers['content-length'];
var uploadedBytes = 0;
request.on('readable', function() {
var chunk = null;
while(null !== (chunk = request.read())) {
uploadedBytes += chunk.length;
var progress = (uploadedBytes / fileBytes)*100;
response.write("progress: " + parseInt(progress,10) + "%\n");
}
});
request.pipe(newFile);
}).listen(8080);
To do list: play around with
http://gulpjs.com.