This talk will be focused on several issues related to code implementations that uses asynchronous callbacks.
If you are new to the whole Node.js® stuff I can recommend Mixu's Node book a short condensed book about things You need to know.
For further reading I can recommend The 4 keys to 100% uptime with Node.js blog post about application error handling and graceful process termination.
Blocking code example shows how javascript runtime behaves in several situations. And displays difference between process.nextTick
and setImmediate
.
Read JOSN file code example shows how callback supported function can be implemented. Example shows how error from readFile
is passed thru to callback and new possible JSON.parse
exception is handled.
var readJSON = require('./lib/read-json');
readJSON('some.json', onread);
readJSON('notfound.json', onread);
readJSON('parseError.json', onread);
function onread(error, result) {
console.log();
if (error) {
console.error('ERROR:');
console.error(error.stack);
console.error();
} else {
console.log('RESULT:');
console.log(result);
console.log();
}
}
Read JOSN files code example shows common pattern with multiple parallel async I/O calls, where execution is finished then all pending jobs are done (pending
value goes to zero). In addition to that execution is stopped if error occurs on any of currently read files and first error is passed to callback.
var readJSONFiles = require('./lib/read-json-files');
var files = [
'some.json',
'notfound.json',
'parseError.json'
];
readJSONFiles(files, onread);
function onread(error, result) {
console.log();
if (error) {
console.error('ERROR:');
console.error(error.stack);
console.error();
} else {
console.log('RESULT:');
console.log(result);
console.log();
}
}
Read JOSN files code example demonstrates situation where callback can receive third argument as additional information object. All errors occurred when reading files are collected to warnings
object and passed as third argument to callback.
In this example callback will never receive first error
argument.
var readJSONFilesForgiving = require('./lib/read-json-files-forgiving');
var files = [
'some.json',
'notfound.json',
'parseError.json'
];
readJSONFilesForgiving(files, onread);
function onread(error, result, warnings) {
console.log();
if (error) {
console.error('ERROR:');
console.error(error.stack);
console.error();
} else {
console.log('RESULT:');
console.log(result);
console.log();
console.warn('WARNINGS:');
console.warn(warnings);
console.warn();
}
}
Previous examples has common callback handling mistake of not handling 'requested nothing - do nothing' situation. If you pass empty array to readJSONFiles
function callback never be called and execution of program will stop without any notice or farewell.
This issue is fixed in following example where if empty file list is passed callback is called on process next tick. This is done this way because callback is expected to fire after synchronous code finishes.
var readJSONFilesFixed = require('./lib/read-json-files-fixed');
var files = [];
readJSONFilesFixed(files, onread);
function onread(error, result, warnings) {
console.log();
if (error) {
console.error('ERROR:');
console.error(error.stack);
console.error();
} else {
console.log('RESULT:');
console.log(result);
console.log();
console.warn('WARNINGS:');
console.warn(warnings);
console.warn();
}
}
Example shows how both readJSONFiles
and readJSONFilesForgiving
can be implemented by using extended EventEmitter
object with abort
method.
Helper functions reduced to JSONReader
instance creation and several event listeners. Previously discussed code is moved to _readerStart
function and changed to accept JSONReader
object instead of callback.
Example also shows common practice of delaying code execution in JSONReader
constructor by using process.nextTick
. This is done to prevent any events to be triggered before event listeners are attached to object.
var reader = require('./lib/json-reader');
var files = [
'some.json',
'notfound.json',
'parseError.json'
];
reader.readJSONFiles(files, onread);
reader.readJSONFilesForgiving(files, onread);
function onread(error, result, warnings) {
console.log();
if (error) {
console.error('ERROR:');
console.error(error.stack);
console.error();
} else {
console.log('RESULT:');
console.log(result);
console.log();
console.warn('WARNINGS:');
console.warn(warnings);
console.warn();
}
}
Streams in node deserves separate talk. The idea of stream is based upon Unix Shell Script '|' (pipe) operator. streem-upper and streem-hr examples shows two simple transform stream implementations that can be run node lib/stream-upper | lib/stream-hr
together.