Quality Reads

Wednesday, February 13, 2008

Synchronous Service Calls in AS3

Tired of dealing with an asynchronous services in Flex? Here's a solution that can be helpful in certain situations.

Start off with a simple user interface bound to a Cairngorm-style ModelLocator Singleton:

<mx:Grid dataProvider="{ModelLocator.getInstance().myDP}"/>
<mx:Button label="Click here" click="dispatchEvent(new Event('getDP'))" />


Then Create a Controller Class that listens for the click event and calls a service:

class SimpleController{

function SimpleController(){
Application.application.AboveViewHere.addEventListener('getDP', onGetDP );
}

private function onGetDP ( e:Event ):void{
var dpService:DPService= new DPService();
ModelLocator.getInstance().myDP= dpService.getDP();//binds ArrayCollection reference from service to ModelLocator
}
}


Finally here's the service Class:

public class DPService(){

function DPService(){}

private var data:ArrayCollection;
public function getDP(){
var loader:URLLoader = new URLLoader();
loader.addEventListener("complete", onComplete);
loader.load(new URLRequest("someURL.aSweetExtension"));

data = new ArrayCollection();//create the ArrayCollection reference here

return data;//return the reference to the controller
}

private function onComplete( e:Event ):void{//get called when the service returns
var loader:URLLoader = e.target as URLLoader;
//loop over returned data and populate ArrayCollection
for each( var xml:XML in loader.data.items ){
data.addItem( xml.someProperty );//add data to ArrayCollection
}
//no need to return anything or dispatch an Event
}
}


Using this approach, you don't need to handle services asynchronously because of intelligent object reference management. We created the reference to the resulting data in the call to "getDP", Flex's data binding system will take care of the rest for us. This is a handy approach if you're binding the data directly to the UI since it can greatly simplify your controllers. In a situation where there's an error with the service, you can localize your error handling to one function in the controller in most situations.

Happy coding.

Cheers,
Todd

PS - I'm using Singleton's here for simplicity of the example. I don't suggest using them this way in an actual application. Proper MVC architecture wasn't within the scope of this article. Check out PureMVC, if you interested in learning more about Flex/AS3 architecture.

5 comments:

Carlos Sola-Llonch said...

you have a possible race condition.

your data ArrayCollection object is created AFTER your call to loader.load(...) and in the oncomplete you then reference data which may possibly still be null.

Todd Cullen said...

Actionscript is single threaded. The entire function body will always be executed before handling the result of a service call. But for what its worth, there's no reason not to move that line up above the service call.

Priyank said...

Nice post. If possible can you share the code with us on the blog. Thanks

Anonymous said...

This doesn't work at all. If you step through this exact example you'll see that the getDB() function returns before the data (ArrayCollection) is populated with anything. Am I missing something here?

Thanks,
JC

Todd Cullen said...

JC this example only works with Flex databinding. So if you are using regular flash this wouldn't work for you. You are correct that this isn't "true" synchronous Flex services. That doesn't exist in Flash (in the player internals, a new thread is created to execute service requests).
If you are using Flex, you create a new ArrayCollection in the first function and set that in the ModelLocator. Then, once the service returns you add items to the empty collection, filling it up. This will cause the ArrayCollection to dispatch "collectionChange" events, which Flex will use under the hood to rebind the data to any UIComponent you may have assigned it to.
Hopefully that makes sense. Sorry for the delay in getting back to you.