meets

callbacks

What we will See


  • Continuations
  • Practical session: objects
  • Callbacks
  • Practical session: removing callbacks

Continuations


Continuation-Passing Style

Continuations


Functional Programming Style


CPS: Continuation-passing Style


Also known as: callbacks


State is passed as parameters and clausures


Great for asynchronous calls!

Example: Socket server


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
Parameters in bold
Clausures in cyan

Another View of the Same


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);

Three different levels of execution

ASYNC


Library for async execution


Based on continuations


  • async.series(): runs functions one after the other
  • async.parallel(): runs everything at the same time
  • async.waterfall(): passes results from one to the next


Fuente: async

Async.series()


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

Practical session 1: async


Use the async module to read a number of values

and add them up


Careful with clausures!

Tech Spec


Read all the files:


  • http://stats.mediasmart.es/bulk/test-2014/account-01.json
  • ...
  • http://stats.mediasmart.es/bulk/test-2014/account-05.json


For each one parse the JSON file,

read the "Debt" field,

and aggregate all values

Sample


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);
    }
});

Now do that 5 times

Second level function


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);
        });
    };
}

Good job!


Callbacks


Introduction to "Callback Hell"

 simplecached test

'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

Callback Hell


The fearsome callback pyramid


Infinite nested continuations


Abusing your lambdas (anonymous functions)


Hard to follow, debug and refactor


Modern GOTOs?

Simple Solutions


Use named functions


Create an object with named attributes

Clausures become attributes


Use async: async.waterfall()


Use events: EventEmitter

Advanced Solutions


Promises


Reactive Programming


Generators: already there


Coroutines: not yet there

Practical Session 2: Removing callbacks


Review the callback pyramid


Original code: socket server


Transform into an object with attributes


Not more than two nested callbacks

socket server


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
Parameters in bold
Clausures in cyan

Success!


Thanks!





IronHack meets Node.js: Callbacks

By Alex Fernández

IronHack meets Node.js: Callbacks

IronHack webdev bootcamp Aug 2015: continuations and callbacks.

  • 1,187

More from Alex Fernández