Koa Framework, Async JavaScript and Generators

Posted February 13, 2015 - 3 min read
Topics:  

Within this post I will share with you my experience of learning Koa which is a Node.js framework that I have recently worked with.

The whole idea behind this framework is to make working with callbacks and asynchronous tasks more enjoyable and cleaner.

You may have heard about Callback Hell and how to use promises to make your code cleaner and more structured.

Well, the authors of Koa did one more step forward in that direction using ES6 built-in generators to handle asynchronous tasks.

An example of Koa using a generator:

var koa = require('koa');
var app = koa();

app.use(function *(){
  var h = yield function hello() {
    var defer = Promise.defer();
    setTimeout(function () {
      defer.resolve('hello');
    }, 1000);
    return defer.promise;
  };

  var w = yield function world() {
    var defer = Promise.defer();
    setTimeout(function () {
      defer.resolve('world');
    }, 2000);
    return defer.promise;
  };

  this.body = `${h} ${w}`;
});

app.listen(3000);

In the above example, each expression provided to yield returns a promise for which Koa has to wait until it completes and then resumes the provided generator passing the resulting value.

So we end up into consuming multiple async operations using a syntax that resembles to synchronous tasks.

In case of a promise failure, Koa simply throws an error using the generator throw method ending the execution of the generator.

Therefore, we can catch the thrown exceptions just like in a synchronous code.

Koa from behind the scenes

In the above example we yielded promises. Could we yield any other different value? I could not answer to that question.

It turned out that Koa is built on the top of the co library (a Node.js module). The picture now is more clear.

In fact, we can yield any value that the co library can handle.

Co library

co is a nice Node.js module that combines promises, thunks, etc. with generators to help us create asynchronous tasks without callbacks.

To understand how co works, I want to illustrate the picture with some code samples that will help us to get the idea behind it.

'use strict';

var Promise = require('bluebird');

//
function process(gn, result, cb) {
  let next = gn.next(result);

  if (next.done) {
    return cb(next.value);
  }

  next.value.then(
    result => process(gn, result, cb),
    err => gn.throw(err)
  );
}

// Let's consider this function as a miniature of the whole co library.
function co(generator) {
  var p = Promise.defer();
  process(generator(), null, function (r) {
    p.resolve(r);
  });
  return p.promise;
}

//
co( function* test(){
  var h = yield function hello() {
    var defer = Promise.defer();

    setTimeout(function () {
      defer.resolve('hello');
    }, 1000);

    return defer.promise;
  };

  var w = yield function world() {
    var defer = Promise.defer();

    setTimeout(function () {
      defer.resolve('world');
    }, 2000);

    return defer.promise;
  };

  return `${h} ${w}`;
}).then(function (r) {
  console.log(r);
});

A basic co library demonstration

Now you got the picture.

co processes generators the same way as shown in the above example, except that it is a more advanced library and supports many yieldable objects other than promises (thunks, arrays, objects, generators).

Conclusion

This was my first attempt to understand Koa as a micro framework from the architectural point of view.

If you want to learn more about Koa you should visit the framework’s website where you can find more information and examples.