7 Tips for new node.js users
I've been working with node.js almost a year. Here's my tips for new node.js users.
Make it Named
JavaScript allows unnamed objects and functions. Generally, it simplfies some naming work and makes lambda shroter.
However, named function and object is firendly for you to debug and profile. Here I steal a picture from an article of Chrome DevTool:
Pretty cool, huh?
Unreference earlier
JavaScript's GC works like a reference counter. Object can be released only if there's no reference to it anymore.
You may have clousres or global object reference a object for temperoray use. If so, dereference it earlier:
var some_var = new net.Server();
// other code...
var i_want_it_temperoray = some_var;
some_operation(i_want_it_temperoray);
i_want_it_temperoray = null; // derefernce
// other code...
Sometimes, if one variable inside closure cannot be released, the whole closure cannot be released by GC, either, which, leads to every variable cannot be released.
There're some good tools to monitor memory, such as heapdump, webkit-devtools-agent. This makes sense that you should use more named functions as said beyond.
No copy
Code in my project changes very fast, some code is copied from here to there and reuse. But usually after this, some variables should rename or already renamed is forgotten, results in some strange behaviours.
Typing the code yourself is good. Since IDE or editors may have a chance to check this for you.
Careful when involve new modules
There are thouthands modules of Node.js. But some of them actually are dead. Node.js changes its API frequently between major versions. Module works under v0.8.x may abort under v0.10.x.
Before you involve a new module, you should check its pr list or issue list, to ensure it has no serious bug or it is still under maintenance.
Use async or promise
Node.js is based on callbacks. It's very easy to write nested callback function calls. Callback is good for asynchronous, but bad for code management and maintenance and it's hard to debug.
If you start wirting code have more than 3 callbacks nested, then you should consider async.js or some promise libraries.
This is a simple sequence of function calls using async.js:
async.auto([
'init_logger': function(done){
set_handlers_to_logger(done);
},
'load_config': ['init_logger', function(done){
load_my_config(done);
}],
'init_database': ['load_config', function(done){
connect_to_db_here(done);
}],
// open_cache here doesn't require database opertion
'open_cache': ['load_config', function(done){
open_cache_here(done);
}],
// warm_up usually fetch some data from database
// notice warm_up works only if 'init_database' AND 'open_cache' is done
'warm_up': ['init_database', 'open_cache', function(done){
fetch_some_data(done);
}],
'init_routers': ['load_config', function(done){
install_routers(done);
}],
'emit_out': ['warm_up', 'init_routers', function(done){
notify_others(done);
}]
], function(err) {
if(err){
// handle possible err here
}
});
I'm not famliar with promise libraries, such as Q, but I guess we can have a similar readble calls:
Q.nfcall(function init_logger(){
set_handlers_to_logger();
})
.then(function load_config(){
load_my_config();
})
.then(function init_database(){
connect_to_db_here();
})
.then(function open_cache(){
open_cache_here();
})
.then(function warm_up(){
fetch_some_data();
})
.then(function init_routers(){
install_routers();
})
.then(function emit_out(){
notify_others();
})
.catch(function (error) {
// handle possible err here
})
.done();
Most of the time, it's your own flavor to choose. Generally, async.js is very easy to start, while Q is much more flexable.
For example, functions in async.js is not nested so that it's a bit harder to capture variables, while you can nest
then
clouses in Q..
I was plan to add a example for direct nested callbacks, but I really doubt if I can make it right.
Do you prefer CoffeeScript? Then you should give IcedCoffeeScript a try. IcedCoffeeScript provides powerful key words: await
and defer
. These two can be very useful when you want a asynchronous feel on language level. Basically, you can have almost all control flow async. But I'm not an expert on IcedCoffeeScript, I suggest you go check its document for detail and examples. Thanks brettof86 for mentioning this brilliant idea.
Client may be very slow
Node.js is too ideal sometime, so it may be very easily to write a chat-like program like below:
var net = require('net');
var clientList = [];
var server = net.createServer(function(c) { //'connection' listener
console.log('server connected');
clientList.push(c);
c.on('end', function() {
console.log('server disconnected');
unpipe_all(c, clientList);
remove_from(c, clientList);
});
clientList.forEach(function(item){
item.pipe(c); // note this line
c.pipe(item); // note this line
});
});
server.listen(8124, function() { //'listening' listener
console.log('server bound');
});
```javascript
The whole model is not that wrong, but you should notice that network of client is not that good as others. When use `pipe` here, lots of data will stay in memory until the client really receieve the data. Our assumption is client is fast, but that's only assumption!
One way to solve this is to have a delegate and make a conditional buffer mechanism internally, like below:
```javascript
Delegate delegate;
clientList.forEach(function(item){
delegate.append(item);
//internally, this delegate has an associated buffer as well as a disk file
//and use buffer when possible, or file if buffer is full.
});
Welcome if you have other ideas to solve this. I'm quite open on this ;)
Emit when finished, or even later
Most small Objects are consructed immediately, while larger ones may be constructed asynchronous.
Talking about construct any object asynchronously, you'd best emit event to notify when finished. If you follow this, then you'd also notice a very helpful statement in Node.js's document:
This is important in developing APIs where you want to give the user the chance to assign event handlers after an object has been constructed, but before any I/O has occurred.
function MyThing(options) { this.setupOptions(options); process.nextTick(function() { this.startDoingStuff(); }.bind(this)); } var thing = new MyThing(); thing.getReadyForStuff(); // thing.startDoingStuff() gets called now, not before.
Typical way here is to use process.nextTick
to send event in constructor. Example:
function SomeTCPServer(options) {
var self = this;
// other init work, and maybe async
process.nextTick(function(){
self.emit('ready');
});
}
// other code..
var server = new SomeTCPServer(ops);
server.on('ready', function when_ready(){
// do something
});
Using process.nextTick
, when_ready
has the chance to be bound before the event emitted.
Maybe more?
This is what I can conclude at this moment. I'm not that experience on node, but I think this may be helpful for people first meet node :) Please feel free to comment and point out any faults I've made.
Written by liuyanghejerry
Related protips
10 Responses
Nice tips, it would be better if you double check the code first. There's code separation at the "chat-like" code section.
otherwise, nice tips :)
nice article, thanks for your sharing.
<a href="/ibnutri">@ibnutri</a>
Thanks, already fixed that ^^
Thanks for this advice.
Rather than async.js check out Iced Coffeescript which is supposedly getting merged into coffeescript eventually.
<a href="/brettof86">@brettof86</a> Thank you! I just added into the article ;)
@brettof86 and @liuyanghejerry - totally agree about IcedCoffeeScript!
I've been using it for almost a year in my projects and I couldn't imagine writing Node.js apps without it. It just "blends naturally" with your app code and makes it look a lot more comprehensible...
I've also wrote a small "protip" on that, couple of months ago :-).
@brettof86 - You were saying Iced Coffeescript which is supposedly getting merged into coffeescript eventually. Would be great to see it happening, but... do you have a source for that?...
@brettof86 - that's what I knew. However, Max Krohn said on a number of occasions (including on this thread on stackoverflow) that he's planning to support ICS indefinitely...
So I'll definitely choose to rely on ICS for my future Node.js projects :-).
@fardjad - node-fibers is interesting, though it has its issues, but for people favouring CS / Ruby style syntax, I'd still say ICS is the best bet :-)