Functional Programming Style
CPS: Continuation-passing Style
Also known as: callbacks
State is passed as parameters and clausures
var net = require('net');
var port = 1702;
var server = net.createServer(function(connection) {
console.log('Connection to %s open', port);
connection.write('Hello?\r\n');
connection.on('data', function(data) {
if (String(data).trim() != 'hello) {
connection.write('ERROR\r\n');
} else {
connection.end('world\r\n');
console.log('Connection to %s closed', port);
}
});
});
server.listen(port);
Continuations in yellow
var net = require('net');
var port = 1702;
var server = net.createServer(function(connection) {
console.log('Connection open');
connection.write('Hello?\r\n');
connection.on('data', function(data) {
if (String(data).trim() != 'hello') {
connection.write('ERROR\r\n');
} else {
connection.end('world\r\n');
console.log('connection closed');
}
});
});
server.listen(port);
Library for async execution
Based on continuations
async.series()
: runs functions one after the otherasync.parallel()
: runs everything at the same timeasync.waterfall()
: passes results from one to the next
Fuente: async
Example:
// an example using an object instead of an array async.series({ one: function(callback){ setTimeout(function(){ callback(null, 1); }, 200); }, two: function(callback){ setTimeout(function(){ callback(null, 2); }, 100); } }, function(err, results) { console.log('Results: %j', results); });Source: async
Use the async module to read a number of values
and add them up
Careful with clausures!
Read all the files:
For each one parse the JSON file,
read the "Debt" field,
and aggregate all values
Read a JSON:
var request = require('request'); var url = 'http://stats.mediasmart.es/bulk/test-2014/account-01.json'; request(file, function(err,res,body) { if (!err && res.statusCode==200) { body = JSON.parse(body); console.log('Debt: %s', body.Debt); } });
Function that returns a function (which reads JSON):
var request = require('request'); function getDebtReader(url) { return function(callback) { request(file, function(error, result, body) { if (error || result.statusCode != 200) { return callback('Could not read ' + url); } body = JSON.parse(body); return callback(null, body.Debt); }); }; }
'use strict'; var client = require('./lib/client.js'); var options = { port: 11311, host: 'localhost' }; var client = new client.Client(options, function(error) { console.assert(!error, 'Could not open connection'); var key = 'testing'; var value = 'real value'; client.set(key, value, function(error, result) { console.assert(!error, 'Error setting key'); console.assert(result, 'Could not set key'); client.get(key, function(error, result) { console.assert(!error, 'Error getting key'); console.assert(result == value, 'Invalid get key'); client.delete(key, function(error, result) { console.assert(!error, 'Error deleting key'); console.assert(result, 'Could not delete key'); client.close(function(error) { console.assert(!error, 'Error closing client'); }); }); }); }); });
Source: simplecached
The fearsome callback pyramid
Infinite nested continuations
Abusing your lambdas (anonymous functions)
Hard to follow, debug and refactor
Use named functions
Create an object with named attributes
Clausures become attributes
Use async: async.waterfall()
Use events: EventEmitter
Review the callback pyramid
Original code: socket server
Transform into an object with attributes
Not more than two nested callbacks
var net = require('net');
var port = 1702;
var server = net.createServer(function(connection) {
console.log('Connection to %s open', port);
connection.write('Hello?\r\n');
connection.on('data', function(data) {
if (String(data).trim() != 'hello) {
connection.write('ERROR\r\n');
} else {
connection.end('world\r\n');
console.log('Connection to %s closed', port);
}
});
});
server.listen(port);
Continuations in yellow