Don't Call Back, I'll Just Wait Here

JavaScript has non-blocking async i/o



How does that get us callbacks?



some_asyncish_task(24, 'foo', function callback(err, thing) {
    if (err) return console.log(err);
    console.log('I\'m in another task?!');
});
console.log('I\'m in this task!');

How does that get us a node.js web server?



const port        = 3000;
const http        = require('http');
const request_lib = require('request');
let last_data     = '';

function processPost(request, response, callback) {
    var queryData = "";
    if (typeof callback !== 'function') return null;

    if (request.method == 'POST') {
        console.log('Handling new POST data');
        request.on('data', function(data) {
            queryData += data;
            if (queryData.length > 1e6) {
                queryData = "";
                response.writeHead(413,
                    {'Content-Type': 'application/json'}
                ).end();
                request.connection.destroy();
            }
        });

        request.on('end', function() {
            request.post = JSON.parse(queryData);
            callback();
        });

    } else {
        response.writeHead(405,
            {'Content-Type': 'application/json'});
        response.end();
    }
}

function requestHandler(request, response) {  
    if (request.method == 'POST') {
        processPost(request, response, function() {
            last_data = request.post;

            response.writeHead(200,
                'OK', {'Content-Type': 'application/json'});
            response.end();
        });
    } else if (request.method === 'GET') {
        response.writeHead(200,
            'OK', {'Content-Type': 'application/json'});
        response.end(JSON.stringify(last_data));
    } else if (request.method === 'PUT') {
        request_lib({
            method: 'PUT',
            url: 'http://httpbin.org/put'
        }, function(err, res, body) {
            if (err) {
                response.writeHead(500,
                    {'Content-Type': 'application/json'});
                response.end();
            }
            response.writeHead(200,
                'OK', {'Content-Type': 'application/json'});
            response.end(body);
        });
    } else {
        response.writeHead(405,
            {'Content-Type': 'application/json'});
        response.end();
    }
}

const server = http.createServer(requestHandler);

server.listen(port, (err) => {  
    if (err) {
        return console.log('something bad happened', err);
    }
    console.log(`server is listening on ${port}`);
});

How does that get us to express?



const port       = 3000;
const express    = require('express');
const request    = require('request');
const bodyParser = require('body-parser');
const server     = express();
let last_data    = '';

server.use(bodyParser.json());

function log_new_data(req, res, next) {
    console.log('Handling new POST data');
    next();
}

server.get('/', function (req, res) {
    res.status(200).send(last_data);
});

server.post('/', log_new_data, function (req, res) {
    last_data = req.body;
    res.status(200).end();
});

server.put('/', function (req, res){
    request({
        method: 'PUT',
        url: 'http://httpbin.org/put'
    }, function(err, response, body) {
        if (err)
            return res.status(500);
        res.status(200)
            .send(JSON.parse(body));
    });
});

server.listen(port, (err) => {  
    if (err) {
        return console.log('something bad happened', err);
    }
    console.log(`server is listening on ${port}`);
});

How are callbacks awkward?



How do promises help callbacks?



function some_promise_task(a, b) {
    return new Promise(function(res, rej) {
        some_asyncish_task(24, 'foo', function callback(err, thing) {
            if (err) return rej(err);
            res(thing);
        });
    });
}
some_promise_task(24, 'foo')
    .then(function(thing) {
        console.log('I\'m in another microtask!');
    })
    .then(function() {
        return some_second_promise();
    })
    .then(function() {
        console.log('I\'m in another microtask!');
    })
    .catch(function(err) {
        console.log('I\'m in another microtask!');
    });
console.log('I\'m in this task!');

How are promises awkward?



How do generators work?



How does that get us Koa 1.x?



const port    = 3000;
const koa     = require('koa');
const request = require('koa-request');
const koaBody = require('koa-body');
const server  = koa();
let last_data = '';

server.user(koaBody());

server.use(function *log_new_data(next){
    if ('POST' == this.method)
        console.log('Handling new POST data');
    yield next;
});

server.use(function *response(next){
    if ('GET' == this.method)
        this.body = last_data;
    yield next;
});

server.use(function *posted_data(next){
    if ('POST' == this.method) {
        last_data = this.req.body;
        this.response.status = 200;
    }
    yield next;
});

server.use(function *put_proxy(next){
    let response = {};

    if ('PUT' == this.method) {
        response = yield request({
            method: 'PUT',
            url: 'http://httpbin.org/put'
        });
        this.body = JSON.parse(response.body);
    }
    yield next;
});

server.listen(port, (err) => {  
    if (err) {
        return console.log('something bad happened', err);
    }
    console.log(`server is listening on ${port}`);
});

How does that get us async/await



How does that get us koa 2.x?



const port    = 3000;
const Koa     = require('koa');
const request = require('koa2-request'); 
const koaBody = require('koa-body');
const server  = new Koa();
let last_data = '';

server.use(koaBody());

server.use(async (ctx, next) => {
    if ('POST' == ctx.method)
        console.log('Handling new POST data');
    await next();
});

server.use(async (ctx, next) => {
    if ('GET' == ctx.method)
        ctx.body = last_data;
    await next();
});

server.use(async (ctx, next) => {
    if ('POST' == ctx.method) {
        last_data = ctx.request.body;
        ctx.response.status = 200;
    }
    await next();
});

server.use(async (ctx, next) => {
    let response = {};

    if ('PUT' == ctx.method) {
        response = await request({
            method: 'PUT',
            url: 'http://httpbin.org/put'
        });
        ctx.body = JSON.parse(response.body);
    }
    await next();
});

server.listen(port, (err) => {  
    if (err) {
        return console.log('something bad happened', err);
    }
    console.log(`server is listening on ${port}`);
});

More things to read