The other day at work we needed to re-think the way we were handling asynchronous tasks. In essence, we needed a way to (cleanly!) ensure one task was complete before moving on to the next. Very casually, my boss suggested we implement a ‘Promise’ pattern. Feeling a bit like a fool for having no idea what he meant, I did a bit of Googling and discovered in Actionscript land it’s a very rare concept, so I thought I’d take a bit of time and write about it (mainly to better understand it myself).
Before diving into what promises are, though, I’ll take a look at the problem they solve. What it boils down to is handling asynchronous tasks (such as asset loading, for example) in a clean, intelligent fashion. In the world of Flash and Actionscript, you’ll typically find two strategies for this type of behavior: a ‘bulk loading’ approach, and an ‘event chaining’ approach.
In bulk loading, you’ll usually see a BulkLoader object which gets passed tasks (asset loading tasks typically). It’s usually left up to the BulkLoader to sort out the types of tasks (is it loading an xml file or a png file) and to keep track of when it’s complete. Normally you would see a numLoaded variable increased on every successful load which is then compared to a numToLoad variable. If they are equal, a ‘complete’ event is dispatched. If anything fails along the way, an ‘error’ event is dispatched. This isn’t a bad way to go (I’ve done it more times than I can count), but it has one major drawback: what if you need tasks performed in a very specific sequence and tasks in the middle of the sequence depend on the output of previous tasks (e.g. what if you load an xml or json file and values within it are used to load additional objects)? Now it would be possible to build an intelligent BulkLoader object that performs tasks in the order in which they’re added passing output to input, but that could become an unmanageable mess real quick like.
More often than not, this is where the event chaining approach comes in. Let’s say, for the sake of argument, you’re working on a game and in order for that game to start successfully, it must perform these tasks in this order:
- Load graphics (e.g. sprite sheet(s))
- Load sounds
- Load player info
- Load high scores
- Initialize the game using all of the above
Only then can the game start and if any of those tasks fail, an error message should be shown and the sequence should start again. Typically, you’ll see a task list like this chained up using ‘onSuccess’ and ‘onError’ callback methods. Something sorta like this:
private function onError():void
// The sequence has failed. Try again
private function onGraphicsSuccess(gdo:GameDataObject):void
this.assets.getSounds(onSoundSuccess, onError, gdo);
private function onSoundSuccess(gdo:GameDataObject):void
this.player.getPlayerInfo(onPlayerInfoSuccess, onError, gdo);
private function onPlayerInfoSuccess(gdo:GameDataObject):void
this.game.getHighScores(onHighScoreSuccess, onError, gdo);
private function onHighScoreSuccess(gdo:GameDataObject):void
Again, there is nothing inherently horrible about this approach. Like the bulk loading method, I’ve done it a countless number of times and it always works. As you can see though, as your code base grows, this approach can quickly become unmanageable and messy – especially if you need to add or remove a task from the middle of the sequence.
This is where the ‘Promise’ pattern (or construct or whatever its correct designation may be) comes in. The ‘Promise’ approach takes the above chaining method and codifies it into a neat and readable api. It does so by exposing a ‘then()’ method which accepts your onSuccess and onError callbacks and returns a new Promise object. Using a Promise, the example above can be re-written to look like this:
.then( this.player.getPlayerInfo )
.then( this.game.getHighScores )
.then( this.game.init )
.then( onLoadSequenceComplete, onLoadSequenceError );
Just take a moment to look at how clean and elegant that is. It reads like a simple to-do list. Even a non-coder can see exactly how to add a new task to the middle of that sequence. Now the inner workings of a Promise are slightly more complex, but not much. Essentially when a Promise completes, it should be ‘resolved’ which will automatically call its onSuccess callback. Likewise, should a promise fail, it should be ‘rejected’ which will call its onError callback, which, in turn, should throw an error (a try..catch block within the promise construct will allow you to gracefully handle the thrown error).
And that is really all there is to it.
I know I’ve pretty much just glossed over the usage and explanation here, and there is, obviously, much more to promises, but there are better places for more details, so here’s a quick list:
the best AS3 Promise implementation I’ve come across
a complete example I put together using the above implementation (on Wonderfl)
a good ol’ Wikipedia article
a very good in depth article