Bert Belder


aka @piscisaureus


node and libuv

http://bit.ly/wrongcode

            
var http = require('http');
var cache = {};

function curl(url, cb) {
  if (cache[url])
    return cb(null, cache[url]);

  var data = '';

  http.get(url, function(res) {
    res.setEncoding('utf8');

    res.on('data', function(s) {
      data += s;
    });

    res.on('end', function() {
      cache[url] = data;
      cb(null, data);
    });

    res.on('error', function(err) {
      cb(err);
    });
  });
}

// Usage:
curl('http://www.google.com', console.log);
            
          

node.js 2.0

"Future of asynchronous programming in node"

"The future of node streams. We need to talk"

"Callbacks as our Generations' Go To Statement"

              
  list 30-70
  30 rem Paint character
  40 x = 4
  50 y = 5
  60 r = 80
  70 GOTO 1000
  80 rem Done

  list 1000-1030
  1000 rem Subroutine for painting -☻-
  1010 LOCATE y, x
  1020 PRINT "-" + CHR$(2) + "-"
  1030 GOTO r
              
            
              
var waiting = 2;

stream1.on('close', function(err) {
  if (--waiting === 0)
    nextStep();
});

process.on('exit', function() {
  if (--waiting === 0)
    nextStep();
});
              
            
              
TransformStream.prototype.pull = function(data, cb) {
  // Call the user's _transform function.
  var sync = true;

  var transformCb = function(data) {
    if (sync) {
      process.nextTick(function() {
        cb(data);
      });
    } else {
      cb(data);
    }
  };

  this._transform(data, cb);

  sync = false;
}
              
            

  • Double / missed callbacks
  • Not handling errors (effectively)
    The intern wrote the contact form
  • Ad-hoc ironing out of flow control kinks
  • To nextTick or not nextTick
  • "construct" for managing callbacks better
  • express intent and abstraction, not flow control
  • build robust applications
  • un-opinionated
  • doesn't break existing apps/modules
              
try_async {

  // Do async stuff.
  // Eventually:
  succeed_with 42;

  // Or you might:
  throw new Error('Вы облажался');

} catch_finally_async(err, result) {
  // We get here when all async stuff
  // in the try_async block is over.
}
              
            
              
try_async {
  setTimeout(function() {
    // Do async stuff.
    // Eventually:
    succeed_with 42;

    // Or you might:
    throw new Error('Вы облажался');
  }, 100);
} catch_finally_async(err, result) {
  // We get here when all async stuff
  // in the try_async block is over.
}
              
            
            
domain.task(function(callback) {
  // Do async stuff.
  // Eventually:
  callback(null, 42);

  // Or you might:
  throw new Error('Вы облажался');

}).setCallback(function(err, result) {
  // We get here when all async stuff
  // in the try_async block is over.
});
            
          
            
domain.task(function(complete) {
  // Within the outer task.

  domain.task(function(complete) {
    // Within a nested task.
    fs.stat(...);
    http.get(...);

  }).setCallback(err, result) {
    // Back in the outer task
    if (err) throw err;
    clearTimeout(timeout);
    complete(null, result + 1);
  });

  var timeout = setTimeout(function() {
    // In the outer task too.
    throw new Error("It's too slow");
  }, 100);

}).setCallback(function(err, result) {
  // In the global task.
});
            
          
            
var http = require('http');
var cache = {};

function curl(url, cb) {
  domain.task(function(complete) {
    if (cache[url])
      return complete(null, cache[url]);

    var data = '';

    http.get(url, function(res) {
      res.setEncoding('utf8');

      res.on('data', function(s) {
        data += s;
      });

      res.on('end', function() {
        cache[url] = data;
        complete(null, data);
      });
    });
  }).setCallback(cb);
}

// Usage:
curl('http://www.google.com', console.log);
            
          


EventEmitter

            
domain.task(function(callback) {
  var db = mysql.connect(80, 'www.google.com');

  db.sendQuery('SELECT * FROM GUNS');

  db.on('row', function(row) {
    console.log(row);
  });
});
            
          
            
var db = mysql.connect(3306, 'localhost');

domain.task(function(callback) {
  db.sendQuery('SELECT * FROM GUNS');

  db.on('row', function(row) {
    console.log(row);
  });
});
            
          

EventEmitter -> Resource

  • All tasks that have a listener on a resource event are 'vulnerable'
  • You can have an error handler within a task.
  • A resource has a well-defined "end"

            
var db = mysql.connect(3306, 'localhost');

domain.task(function(callback) {
  db.sendQuery('SELECT * FROM GUNS');

  db.on('row', function(row) {
    console.log('row');
  });

  db.on('error', function(err) {
    console.log("I don't care!");
  });
});

db.on('error', function(err) {
  console.log("I don't care either!");
});
            
          
            
domain.task(function OuterTask(callback) {

  domain.task(function InnerTask(callback) {
    // beep
  }).setCallback(err, result) {
    // boop
 });

  setTimeout(function() {
    throw new Error("It's too slow");
  }, 100);

}).setCallback(function(err, result) {
  throw err;
});
            
          
Error: ugly, yellow, no-good keister off my property

Task: InnerTask (lib/server.js:52:3)
    at <anonymous> (lib/index.js:15)
    at invokeTimer (timer.js:110:21)
    at TimerWrap.ontimeout

Task: OuterTask (lib/index.js:1:13)
    at Task.<anonymous> (as _complete) (index.js:14:3)
          

What's next?

Follow me :-)
https://twitter.com/piscisaureus

Talk to me!