Stop Worrying and Learn to Love the Async
This work is © 2014 Rod Vagg and is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Australia License
@rvagg
Rod Vagg
A Quick History of Computing
In the beginning
Programming was about computing
Computers were self-contained number-crunchers
Interactive I/O was late to the game
I/O is not computing
I/O is what computers wait for
I/O is the bottleneck in the majority of programs
But, I/O is usually hidden
System.out.println("Reading file...");
BufferedReader br = new BufferedReader(new FileReader("in.txt"));
try {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null)
sb.append(line + "\n");
System.out.print(sb.toString());
} finally {
br.close();
}
System.out.println("Finished reading file!");
The programmer isn't prompted to consider the costs
I/O is expensive
Class | Operation | Time cost |
---|---|---|
Memory | L1 cache reference: | 1 ns |
L2 cache reference: | 4 ns | |
Main memory reference: | 100 ns | |
I/O | SSD random-read: | 16,000 ns |
Round-trip in same datacenter: | 500,000 ns | |
Physical disk seek: | 4,000,000 ns | |
Round-trip from AU to US: | 150,000,000 ns |
Then ...
User Interfaces
The UI introduced new challenges
I/O in the form of a human
Unpredictable
Very high latency
Sequential programming has limits
Good UI's don't constrain the user
Networked UI's: a perfect storm
User variability delivered over unreliable, high-latency networks
Browsers...
On the web, nothing is synchronous
Always in reactive mode, responding to external events
- React to user events
- React to network events
- React to browser events
Enter JavaScript
JavaScript:
The King of event-driven programming
Born in the browser
Designed to respond to user-events
Timers! BREAKING NEWS: Scrolling text with DHTML!
var p = document.getElementById('scroll');
function scroll () {
var ptxt = p.innerHTML.replace(/ /g, ' ').split('');
ptxt.push(ptxt.shift());
p.innerHTML = ptxt.join('').replace(/ /g, ' ');
}
setInterval(scroll, 100)
Warning: content may trigger intense feelings of nostalgia
JavaScript:
The King of event-driven programming
First-class functions
Closures and scope capturing
Single-threaded
Asynchronicity at the extreme:
Node.js
Async works well for performing many complex, parallel tasks in the browser ...
Why not on the server too?
Synchronous I/O with Node.js
Node.js can be classic
Synchronous file system I/O, on the JavaScript thread
console.log('Reading data...');
var data = fs.readFileSync('in.dat');
console.log('Finished reading data!');
Don't do this
Asynchronous I/O with Node.js
File system I/O is performed on a thread-pool when asynchronous
console.log('Reading data...');
fs.readFile('in.dat', function (err, data) {
// asynchronous
console.log('Finished reading data!');
})
console.log('Not finished reading data...');
Asynchronous I/O with Node.js
Network I/O performed with non-blocking system calls
epoll
or select
depending on platform
Asynchronous I/O with Node.js
Network I/O, always asynchronous, generally event-based
var server = http.createServer()
server.on('request', function (request, response) {
// handle HTTP request
});
server.on('clientError', function () { /* ... */ }});
server.on('error', function () { /* ... */ }});
server.on('close', function () {
console.log('Server shut down');
})
server.on('listening', function () {
console.log('Listening on port 8080');
});
server.listen(8080);
The Callback
The callback
JavaScript embraces the continuation passing style
console.log('Ping!');
function pong () { console.log('Pong!'); }
setTimeout(pong, 100);
console.log('Reading file...');
fs.readFile('in.dat', 'utf8', function (err, data) {
console.log('in.dat contains %d lines', data.split('\n').length);
});
function clickHandler () { alert('Yo!'); }
el.addEventListener('click', clickHandler, false);
The callback
The callback function is the fundamental unit of asynchronous programming in JavaScript
- DOM events
- Browser-based networking
- Basic Node.js I/O operations
- Node.js EventEmitter
- Streams
Even Promises and async utilities for generators are built on callbacks
Embrace the Async
Embrace the Async
Node.js-style callbacks are a great way to represent an asynchronous platform
fs.stat('fooballs.txt', function (err, stat) {
if (err)
// deal with it
else
// get on with it
})
- I/O is in your face, you have to deal with as a special case
- Error-handling is first-class
- The pattern is simple and allows highly compatible modularity
Embrace the Async
Hide asynchronous behaviour at your peril
Use abstractions wisely: for productivity, not to change the fundamental nature of the platform
The world is asynchronous, programming should be asynchronous, learn to think in async