Applying Javascript thinking to AS3 – Anonymous functions

roy.k@wix.com | May 17th 2012 | ActionScript

When Wix started developing the HTML editor, many of the people whose full time job was AS3 development suddenly had to start developing in Javascript as well. We all dabbled in Javascript in our spare time, personal projects etc., but none of us had an opportunity to work full time on production quality code in Javascript before.
Interestingly, after some time in the new predicament, we all came up with the conclusion that there are a set of programming problems that are solved more elegantly when applying techniques that are more common to Javascript than to AS3. Specifically I’d like to discuss anonymous functions.

var f = function(){
    trace('I am a trace inside an anonymous function!')
};

f();

We all knew about anonymous functions before, but we purposefully didn’t use them. Our experience with passing around functions as arguments was that of hard to read and hard to debug code. To battle these shortcomings we reduced to a minimum our usage of function arguments. Where they had to be used we adopted a very verbose strategy. For example, when handling events, we’d create a skeletal event handler that simply called the function that does all the action, thus augmenting readability.

button.addEvent(MouseEvent.CLICK, onMouseClick);
.
.
.
private function onMouseClick(ev:MouseEvent)
{
    openDialog();
}

private function openDialog()
{
    // do the actual dialog opening here.
}

In the above example, it is obvious from the code that clicking on the button opens a dialog. If the dialog’s logic was inside the event handler, it may have taken some code reading to figure out what’s happening. Worse still, if I decided to change the mouse click action but still wanted to retain the dialog opening logic, I’d have to start touching the dialog opening code to do so, risking bugs.
This frame of thought very often kept us with two degrees of separation from a chance to use anonymous functions. For the most part of our AS3 experience, we managed to do quite well without them. However, due to Javascript’s nature, we continuously encountered problems in the new project that are solved much more elegantly with anonymous functions, and avoiding them became a serious burden.

The case for Anonymous functions

An Asynchronous action is a piece of code that executes independently of your normal application flow. Common asynchronous scenarios are a need to call a server, a need to delay an action, wait for some resource to load, etc.

Here’s a simple piece of code, that synchronously creates a list of buttons from some sort of a description class.

private function createButtons(buttonsInfo:Array):void
{
	var buttons:Array = new Array();

	for (var i:int=0; i<buttonsInfo.length; i++)
	{
		var buttonData:Data = buttonsInfo[i] as Data;
		var button:Button = creator.create(buttonData);
		buttons.push(button);
	}

	showButtons(buttons);
}

Now, for some reason, the creator.create function suddenly becomes asynchronous, so I’ll have to rewrite the code to deal with this fact:

private var m_loadCount:int=0;
private var m_buttons:Array;

private function createButtons(buttonsInfo:Array):void
{
	m_loadCount = buttonsInfo.length;
	m_buttons = new Array();
	creator.addEventListener(Event.COMPLETE, onButtonCreated);
	for (var i:int=0; i<buttonsInfo.length; i++)
	{
		var buttonData:Data = buttonsInfo[i].
		creator.create(buttonData);
	}
}

private function onButtonCreated(ev:Event)
{
	var button:Button = ev.target.data as Button;
	m_buttons.push(button)
	m_loadCount--;
	if (m_loadCount==0)
	{
		showButtons(m_buttons);
	}
}

What’s that? Suddenly I have a bunch of new field variables that weren’t there before. In fact, for every piece of data that I am given in the createButtons function, I am going to need to remember it for the onButtonCreated. Worse still, if someone calls createButtons twice, before the loading is complete, I’m going to have to make sure that my new field variables support that. If they don’t, I’m going to have to start thinking of a smarter loading system.

Now, let’s see how the same code can be written using an anonymous function:

private function createButtons(buttonsInfo:Array):void
{
	var buttons:Array = new Array();

	creator.addEventListener(Event.COMPLETE, function(ev:Event){
		var button:Button = ev.target.data as Button;
        buttons.push(button)
		if (buttons.length==buttonsInfo.length)
		{
			showButtons(buttons);
		}
	});

	for (var i=0; i<buttonsInfo.length; i++)
	{
		var buttonData:Data = buttonsInfo[i].
		creator.create(buttonData);
	}
}

Suddenly, the complexity is reduced almost to the same level of the synchronous approach. The reason for the complexity was that the asynchronous nature of creator.create made me break up my original function, and I had to keep its context alive somehow until the buttons finish loading. Using the closure nature of anonymous function, though, the context is automagically stored for me, and I can write the entire code snippet as one cohesive unit again.

In other words, it is often the case with asynchronous actions that the context in which I want to execute the delayed action is the same context in which I executed the original action. Anonymous functions let me keep that context.

Anonymous functions are a great tool to bridge the gap when we have an unnatural context switch. If we find that the callback function is a natural extension of the original function, or that we have to pass around or store many of our local variables in order to recreate the context in which we previously existed, it may be the case that an anonymous function will do the trick more elegantly.


By roy.k@wix.com
Wix

Leave a Reply

We are always looking for excellent people. Browse Jobs Here   

At Wix Engineering we develop some of the most innovative cloud-based web applications that influence our 80+ million users worldwide.

Have any questions? Email academy@wix.com.

Find out what’s coming up at Wix Engineering:

Subscribe to our newsletter for updates, events and more.