Skip to content
Tags

Composable Generators in Javascript

August 8, 2014

I’m getting to work with Node.js for the first time and generally enjoying the change of pace from .Net — especially the part where my full Mocha test suite that includes integration tests runs faster than my souped up i7/SSD box can compile even the simplest .Net library. For my part, I’ve been playing with a new library to support event sourcing user defined with projections by leveraging Postgresql’s native JSON features and embedded Javascript support (not much to see yet, but the code is here if you’re curious).

Since it is Node.js, every action you do that touches the database or file system really wants to be asynchronous. I started by using promises with the Bluebird library, and it was okay for simple cases. However, the code became very hard to follow when I started chaining more than 3-4 operations together. Just this week I finally got a chance to try out the usage of the new ES6 generators feature with my codebase in the places where I was having to combine so many asynchronous operations and I’m very happy with the results so far.

To my eyes, generators are a big improvement in readability over promises. Consider two versions of the same method from my event store library. First up, this is the original version using promises to execute a stored procedure in Postgresql (yep, I said sproc) then transform the asynchronous result to return to the calling client:

	append: function(){
		var message = this.toEventMessage(arguments);

		return pg.connectAsync(this.options.connection).spread(function(client, release){
			return client.queryAsync('select pge_append_event($1)', [message])
				.finally(release);
		})
		.then(function(result){
			return Promise.resolve(result.rows[0].pge_append_event);
		});
	},

Now, see the newer equivalent method using generators with some help from the postgres-gen library:

	append: function(){
		var message = this.toEventMessage(arguments);

		return this.db.transaction(function*(t){
			return (yield t.query('select pge_append_event(?)', message)).rows[0].pge_append_event;
		});
	},

I’m going to claim that the generator version on the bottom is cleaner and easier to write and understand than the version that only uses promises above. Some of that is due to my usage of the postgres-gen library to smooth out the interaction with Postgresql, but I think that not having to nest Javascript functions is a big win (yes I know that Coffeescript or arrow functions with Traceur would help too by making the inline function syntax cleaner, but I’d still rather avoid the nesting anyway).

The biggest difference to me came when I wanted to start dynamically composing several asynchronous operations together. As I said earlier, I’m trying to build an event store library with user defined projections. To test the projection support I needed to repeatedly store a sequence of events and then fetch and evaluate the value of the calculated projection to verify the new functionality. I very quickly realized that embedding the original promise mechanics into the code was making the tests laborious to write and hard to read. To that end, I built a tiny DSL just for testing. You can see the raw code in Github here, but consider this sample:

	scenario('can update a view across a stream to reflect latest', function(x){
		var id = uuid.v4();

                // stores several pre-canned events to the event store
		x.append(id, 'Quest', e1_1, e1_2, e1_3);

                // does an assertion that the named view for the event
                // stream above exactly matches this document
		x.viewShouldBe(id, 'Party', {
			active: true,
			location: 'Baerlon',
			traveled: 16,
			members: ['Egwene', 'Mat', 'Moiraine', 'Perrin', 'Rand', 'Thom']
		});

                // do some more events and check the new view
		x.append(id, e1_4, e1_5);
		x.viewShouldBe(id, 'Party', {
			active: true,
			location: 'Shadar Logoth',
			traveled: 31,
			members: ['Egwene', 'Mat', 'Moiraine', 'Perrin', 'Rand']
		});

	});

Every call to the append() or viewShouldBe() methods requires an asynchronous operation to either post or query data from the underlying Postgresql database. Originally, I implemented the testing DSL above by creating an empty promise, then running that promise through all the “steps” defined in the scenario to chain additional promise operations. Earlier this week I got a chance to switch the testing DSL to using generators underneath the language and that made a big difference.

The first thing to know is that you can embed a generator function inside another generator by using the yield* keyword to designate that you want an inner generator function to be executed inline. Knowing that, the way that I implemented the testing DSL shown above was to create an empty array called steps as a member on a scenario object. In the testing expression methods like the append(), I just pushed a new generator function into the steps array like this:

	this.append = function(){
		var message = client.toEventMessage(arguments);
		self.lastId = message.id;

		// Adding a new generator function
		// to the steps array
		this.steps.push(function*(){
			yield client.append(message);
		});
	}

After the scenario is completely defined, my testing DSL just executes the steps as a single generator function as shown in the code below:

	// I'm using the Bluebird Promise.coroutine()
	// method to treat this single aggregated
	// generator method as a single promise
	// that can be happily executed and tracked
	// by the Mocha test harness
	this.execute = function(client){
		return Promise.coroutine(function*(){
			this.steps.forEach(function(s){
				yield* s;
			});
		});
	}

You can see the commit diff of the code here that demonstrates the difference between using just promises and using a generator to compose the asynchronous operation. The full implementation for the “scenario” testing DSL is here.

As an aside, if you’re playing Design Pattern Bingo at home, my testing DSL uses Fowler’s Nested Closure pattern to first define the steps of a test specification in a nested function before executing the steps. I’ve used that pattern successfully several times for little one off testing tools and seen plenty of other folks do similar things.

 

Using generators as a composable Russian Doll Model with Koa.js

My organization settled on a Node.js based stack for a rewrite of an older .Net system, but when we first started discussing a possible new platform Node.js was almost summarily dismissed because of its callback hell problems. Fortunately, I got a tip on twitter to look at the Koa.js framework and its support for generators to avoid the older callback hell problems and I was sold. Since our timeline is long enough that we feel safe betting on ES6 Harmony features, we’re starting with Koa.js.

One of the things I feel was successful in FubuMVC was our use of what I’ve always called the Russian Doll model. In this model, we could compose common behaviors like validation, authorization, and transaction management into reusable “behaviors” that could be applied individually or conventionally to the handlers for each HTTP route in a similar manner to aspect oriented programming (but with object composition instead of IL weaving or dynamic proxies). One of the things that appeals to me about Koa.js is their usage of generators as a middleware strategy that’s conceptually equivalent to FubuMVC’s old behavior model (the current Connect middleware is “single pass” where the Russian Doll model requires nesting the middleware handlers to do optional before and after operations).

At some point I’d like to experiment with building the FubuMVC “BehaviorGraph” model of configuring middleware per route for better fine grained control, but that’s a ways off.

From → Uncategorized

3 Comments
  1. Clement permalink

    Yes that is very powerful and readable. Very much like c# async/ienumerable, and very much like owin middleware pipeline, which use Func<IDictionary,Task> for each middleware and can chain all async functions together…

Trackbacks & Pingbacks

  1. The Morning Brew - Chris Alcock » The Morning Brew #1670
  2. Dew Drop – August 11, 2014 (#1833) | Morning Dew

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 31 other followers

%d bloggers like this: