Saturday 6 February 2010

AsyncTokenWrapper is Dead, Long Live AsyncTokenWrapper

As I said in the comments of my last post about AsyncTokenWrapper I didn’t like the way I was wrapping AsyncToken and handling the response.

A much neater solution to this is to create and register a custom command factory. You register a command based on the return type of your function. In my case I am returning a ProcessingAsyncToken so I go about registering my factory like this:

GlobalFactoryRegistry.instance.messageRouter.addCommandFactory( 
ProcessingAsyncToken,
new ProcessingCommandFactory()
);

I do this in a support class that implements ContextBuilderProcessor so that I can set this up when I create my context:





My command factory looks like this:

public class ProcessingCommandFactory implements CommandFactory
{
public function createCommand(
returnValue : Object,
message : Object,
selector : * = null
) : Command
{
return new ProcessingCommand( returnValue,
message,
selector
);
}
}

And just returns a new instance of my ProcessingCommand:

public class ProcessingCommand extends AbstractCommand
{

//-------------------------------------------------
//
// Constructor
//
//-------------------------------------------------

public function ProcessingCommand(
returnValue : *,
message : Object,
selector : *
)
{
super( returnValue, message, selector );

_processingToken = ProcessingAsyncToken( returnValue );

_processingToken.token.addResponder(
new Responder( complete, error )
);

start();
}

//-------------------------------------------------
//
// Private Variables
//
//-------------------------------------------------

private var _processingToken : ProcessingAsyncToken;

//-------------------------------------------------
//
// Overridden Methods: AbstractCommand
//
//-------------------------------------------------

protected override function selectResultValue(
result : *,
targetType : ClassInfo
) : *
{
return ( targetType.getClass() != ResultEvent && result is ResultEvent )
? ResultEvent( result ).result
: result;
}

protected override function selectErrorValue(
result : *,
targetType : ClassInfo
) : *
{
return ( targetType.getClass() == Fault && result is FaultEvent )
? FaultEvent( result ).fault
: result;
}

override protected function complete( result : * = null ) : void
{
var argumentsArray : Array;

if ( _processingToken.resultProcessingFunction != null )
{
try{
argumentsArray = _processingToken.resultProcessingArguments.concat();
argumentsArray.unshift( result.result );
result = _processingToken.resultProcessingFunction.apply(
this,
argumentsArray );
}
catch( e : Error )
{
error( e );
}
}

super.complete( result );
}

override protected function error( result : * = null ) : void
{
var argumentsArray : Array;

if ( _processingToken.faultProcessingFunction != null )
{
argumentsArray = _processingToken.faultProcessingArguments.concat();
argumentsArray.unshift( result.fault );
result = _processingToken.faultProcessingFunction.apply(
this,
argumentsArray );
}
super.error( result );
}

}

We just extend AbstractCommand and override the complete and error functions to apply the functions defined in ProcessingAsyncToken before passing the result on. In the constructor we add a responder to the AsyncToken in the return Value.

I call the function as follows:

[Command( type="com.pricklythistle.picasa.event.ListPicasaAlbumsEvent", messageProperties="userID,kind" )]
public function loadUserAlbums(
userID : String,
kind : String = null
) : ProcessingAsyncToken
{
url = StringUtil.substitute( BASE_URL, userID );

return loadUserEntries( kind );
}

I think this is a much neater solution that wrapping an AsyncToken inside another Async token – that had a really bad smell about it!

No comments:

Post a Comment