A customer of mine found a bug my AS3 DataManager when making multiple simultaneous calls. Here is the fix to the bug.
The issue is that while the DataManager allows calls to many methods in a web service, each instance of the DataManager only has a single eventName property. I was able to work around this by making use of hte AsyncToken, which is returned whenever a call to an AbstractOperation (such as a WebService) is made. The AsyncToken is carried along with the request and available when results happen as part of the ResultEvent. Even better, its a dynamic class, so you can add any arbitrary properties on it you want. So, to fix the issue, replace the existing makeRemoteCall method with this:
public function makeRemoteCall(methodName:String,eventName:String, args:Object):void{
trace("DataManager.makeRemoteCall("+methodName+","+eventName+","+args+")");
this.eventName = eventName;
var op:mx.rpc.AbstractOperation = ws[methodName];
ws.addEventListener("result",doResults);
ws.addEventListener("fault",doFault);
if(args){
op.arguments = args;
}
var token:AsyncToken = op.send();
token.eventName = eventName;
}
private function doResults(event:ResultEvent):void{
var e:DataManagerResultEvent = new DataManagerResultEvent( event.token.eventName, event.result);
this.dispatchEvent(e);
}
Now, the event names is stored with the request, so we have access to a different event name for each request made! The completed code looks like this:
package managers {
import flash.events.EventDispatcher;
import mx.rpc.soap.WebService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.AbstractOperation;
import events.DataManagerResultEvent;
import flash.util.*
import mx.rpc.AsyncToken;;
/** DataManager - singleton class which enforces only
a single object is created for eachwsdl. To
access DataManager, use getDataManager(wsdl:String) */
public class DataManager extends EventDispatcher {
private var ws:WebService;
private var eventName:String;
// hashmap of instances for each wsdl
private static var instanceMap:Object = new Object();
private static var methodEventMap:Object = new Object();
public function DataManager(pri:PrivateClass, wsdl:String){
this.ws = new WebService();
ws.wsdl = wsdl;
ws.loadWSDL();
ws.useProxy = false;
}
public static function getDataManager(wsdl:String):DataManager{
if(DataManager.instanceMap[wsdl] == null){
DataManager.instanceMap[wsdl] = new DataManager(new PrivateClass(),wsdl);
}
var dm:DataManager= DataManager.instanceMap[wsdl];
if(dm.ws.canLoadWSDL()){
return dm;
} else {
throw new Error("BAD WSDL:"+wsdl);
}
}
public function makeRemoteCall(methodName:String,eventName:String, args:Object):void{
trace("DataManager.makeRemoteCall("+methodName+","+eventName+","+args+")");
this.eventName = eventName;
var op:mx.rpc.AbstractOperation = ws[methodName];
ws.addEventListener("result",doResults);
ws.addEventListener("fault",doFault);
if(args){
op.arguments = args;
}
var token:AsyncToken = op.send();
token.eventName = eventName;
}
private function doResults(event:ResultEvent):void{
var e:DataManagerResultEvent = new DataManagerResultEvent( event.token.eventName, event.result);
this.dispatchEvent(e);
}
private function doFault(fault:FaultEvent):void{
trace("DataManager.doFault("+fault.fault.faultString+")");
this.dispatchEvent(fault);
}
public override function toString():String{
return "DataManager";
}
}
}
/** PrivateClass is used to make DataManager constructor private */
class PrivateClass{
public function PrivateClass() {
}
}