9/03/2009

On the Importance of being Synchronous: Asynchronous + Actionscript

Dealing with Asynchronous Events Part-2

Damn Blogspot sucks. What the hell? Why haven't they added the 1st new feature in like 5 years? Trackbacks hello???? WTF? I'm not up for cobbling a solution together with greasemonkey, yada, yada, yada. I need a new blog platform. Enough about that let's get to code.

Anyway I wanted to add my fuel to the fire on asynchronous programming. This is a topic I'm very interested in because Actionscript isn't the only language suffering from this. It's very much rooted in classic Computer Science so it's a deep topic. That blog post is old, but it's still something that doesn't have a satisfactory answer yet. Computer scientists have been discussing this topic since the 1970s in one form or another.

I'm very satisfied with my solution to the single request problem. By that I mean making a single round trip to the server and back. Here is roughly how I do asynchronous calls in actionscript.


var tag : String = "Archive";
var loader : URLLoader = defaultLoader();
loader.addEventListener( Event.COMPLETE, function( event : Event ) : void {
var json : Object = JSON.decode(loader.data);
var mail : Array = json.map( funcion( json : Object, index : int, arr : Array ) : Email {
return new Email( json )
} );
var event : DynamicEvent = new DynamicEvent('mail.loaded');
event.mail = mail;
dispatch( event );
} );
loader.load( session.httpGet( '/home/email/', { tag: tag } ) );


Really tight code, and it doesn't feel like the infrastructure for doing the calls are in the way of understanding what's going on. Now I'm using several factory methods to encapsulate common error handling, host name, authentication tokens, etc. Of course all of this can be overridden, but having defaults keeps that code out of the flow of how you work.

The difficult part comes when you need synchronous flow control over asynchronous calls. This only starts to show up with more than one trip to the server. Say for example, server call 1 must complete before server call 2. You can chain them like so:


var loader : URLLoader = defaultLoader();
loader.addEventListener( Event.COMPLETE, function( event : Event ) : void {
var json1 : Object = JSON.decode(loader.data);

// do something with json1

var nextLoader : URLLoader = defaultLoader();
loader.addEventListener( Event.COMPLETE, function( event : Event ) : void {
var json2 : Object = JSON.decode( nextLoader.data );

// do something else with json2, and maybe json1

});
loader.load( session.httpPost( '/home/update', { arg1: json1.arg1 } ) );
} );
loader.load( session.httpGet( '/home/synchronize/', { hashkey: hashkey } ) );


It's doable, but it's getting messy. And quite frankly a little hard to understand. Is that all we need? If so, then we can stop here and be ok. Sadly, no the rabbit hole can get worse and twisted. Say we want to do server call 1, server call 2, or both based on some conditions! And we want to maintain the order call 1 precedes call 2 if call 1 is done. Kinda of like:


var result : Object = null;
if( someExpression ) {
result = executeServerCall1();
}

var result2 : Object = null;
if( someOtherExpression ) {
result2 = executeServerCall2( result.arg1 );
}


Now I want to stop right here and say. Look how easy that was to specify in synchronous code. Jr. programmers can understand that code. All things like conditional logic, control flow, data flow, and more importantly re-usability are all effortless. Just doing simple control flow between asynchronous code is a real challenge.

One thing that I really have trouble with is refactoring logic into a re-usable method that I can call from multiple locations. In synchronous land I can wrap behavior around it doing logic before and after that method. I can easily pass data between in and out. All of these properties lead to reuse and powerful constructs for hiding details. The basis of easy to follow and maintain algorithms.

Adding logic before and after is very hard when the method uses asynchronous calls. I've decided that adding callback objects into the calls it the best route. For example,


public function updateUser( user : User, callback : Function ) : void {
var loader : URLLoader = defaultLoader();
loader.addEventListener( Event.COMPLETE, function( event : Event ) : void {
var json : Object = JSON.decode(loader.data);
var user : User = new User( json );
callback( user );
});
loader.load( session.httpPost( '/user/update/', { id: user.id, email: user.email } ) );
}


I prefer callbacks to using event listener. The main reason for that is event listeners are more long living, i.e. longer than a single method call. If you use event listeners you have to register and unregister between calls, i.e. more mess. If this method is apart of a longer living instance, as I typically do, you could get more than one callback happening. Callbacks are isolated between method calls so they can independent from one another. (There's a lot to discuss here too, but I'll save that for later).

I'm working on my next evolution of this idea to try and build up an architecture to help aid in making multiple round trips to the server in order without adding fuss, and hopefully allowing an outside person to read my code without needing a lobotomy to put my brain in their head. We will have to step away from our friend closure for this to work. But, I want to leave you with this thought.

All of the tools we use today are aided by synchronous control flow. When we remove synchronous flow our tools fall apart. We have very little tools at our disposal to help specify complex flow using asynchronous semantics. Closures are about it, but they aren't enough and fall apart quickly. We need new constructs that aid in asynchronous control flow. Possibly a way to restore synchronous flow, but asynchronous underneath. If we had these constructs we could do this type of work independent from things we typically think of like threads, processes, message passing, etc. Those constructs could be underneath it, but we as programmers would be less involved with their presence.

6 comments:

Andrew Westberg said...

It's been done already. Look at the UMCairngorm project and the EventGenerator class. You can queue up events to fire in sequence or parallel.

http://code.google.com/p/flexcairngorm/

chubbsondubs said...

Oh I'm sure it's been done already. Thanks for the reference, but the project you referenced isn't really applicable to what I'm trying to do. Plus I don't use Cairngorm so it being inside that library isn't much help to me. What I've done is something like that.

I still think I can improve it or strive more more readability, but I think it will never be as readable as synchronous code which is really the larger issue.

It's really not about creating a reusable layer, but to talk about techniques and educate. Put forth ideas and just explore if there are more readable ways of specifying asynchronous code, and how to make it reusable like we can with methods in synchronous coding. I'd argue we are a long way away from reaching the expressiveness and brevity of synchronous code.

The stack is not the asynchronous programmers friend. :-)

Squee-D said...

A state machine is how I handle all asynchrony and it's pretty tight. Where I am employed, we have a state machine that I plan on offering as open source soon, but that aside, it's fairly easy to cobble a state machine together and its really, really tight. For more on some nice state machine frameworks take a look at PureMVC's or AASM for ruby (over on github).

As for callbacks/closures over events. I would say, don't try to mix the paradigm too often. Most services code will use events and it's easy to capture them "one off". Make a little helper class and add a handleEventOnce static method. In this method, register to the event using a closure that calls your callback, but not before removing itself from the dispatcher. That way you can continue using events, and pick and choose wether you want to listen just once, or continuously.

chubbsondubs said...

Could you share some code on your state machine design? Just an example of using it would be cool to see.

I looked at those other examples you provided. I'm curious how define the state machine without getting lots of classes spread around. Again part of this post was just to point out how easy building program flow is easy in synchronous land vs. much more involved in asynchronous land.

We all know the state pattern is applicable here, but that can involve a lot of classes in your careless. Again reuse is important here as well.

Squee-D said...

I'll try to post something ater this week. I'm not in the office until tommorow. To keep the class bloat at a minumum, you probably need to look to using a facade for your model/services or an application controller. I use my state machine in the facade for representation of business domain state, and in controller/views for representation of UI state. Anyway I'll post code and samples to our blog and link you to it later this week.

Ciao.

Unknown said...

UMCairngorm EventGenerator seems to only allow basic command sequencing with no operational logic between commands. To really do anything useful you need to have some kind of branching and flow control.

Here is a related article which may be of interest:

http://gen5.info/q/2008/08/13/converting-a-synchronous-program-into-an-asynchronous-program/

Also this:

http://www.thibaudlopez.net/xhr/Writing%20effective%20asynchronous%20XmlHttpRequests.pdf