Let me go into a little more detail. Let's start by noting that there's no way to make Flash Remoting synchronous. Its nature is to be asynchronous and that cannot be changed. What I want to do is create a layer that performs the interaction with Flash Remoting and handles the results in such a manner as to encapsulate the fact that I am using Flash Remoting.
The typical way to do remoting, once you initialize a service connection, is to make the call something like this:
ActionScript Code:
// Perform the remote call, which in and by itself does not return any value.
service.someRemoteMethod();
// Handle result.
function someRemoteMethod_Result(result)
{
}
// Handle error condition.
function someRemoteMethod_Status(status)
{
}
This code is all implemented as part of a frame's action script or similar. It assumes that the handlers will have access to the presentation components such as dynamic text fields or the like. BTW, we can also use classes to handle such an interaction in which case we create two methods,
onResult and
onStatus, to handle the result of the call and the error thrown by the call, respectively.
I want to be able to separate my presentation logic completely and make it deal with pure AS objects, which model services, that do not, and should not, know anything about the presentation logic. Conversely, the presentation logic will not know that I decided to implement the service using Flash Remoting, or any other technique for that matter. This is simply isolation of responsibilities.
To do this you can create a AS class that encapsulates the remote calls. This serves as your facade layer and the entry point to your service. The base class contains the basics required to deal with the remote calls.
ActionScript Code:
/**
* Base for all classes that require interaction with a service on the server side.
*/
class AbstractServiceConsumer
{
/** Reference to the remote service instance. */
private var service:Object = null;
/** Place-holder for the results returned by the last remote call. */
private var result:Object = null;
/** Flag indicating whether or not the service is waiting for the remote response. */
private var wait:Boolean = false;
/**
* Construct using the given service name.
* @param serviceName name of the service we are to represent.
* @throws an error when it is not able to instantiate the requested service.
*/
private function AbstractServiceConsumer(serviceName:String)
{
// Attempt to get the requested service.
this.service = ServiceLocator.getService(serviceName);
if (null == service)
{
throw (new Error("AbstractServiceConsumer: Unable to instantiate service \"" + serviceName + "\""));
}
}
/**
* Returns the service instance associated to this consumer.
* @return service instance.
*/
public function get Service():Object
{
return this.service;
}
/**
* Returns the result of the last remote call.
* @return result of the last call.
*/
public function get Result():Object
{
return this.result;
}
/**
* Stops processing until either a result or status is received or a predetermined amount of time is expired.
*/
private function waitForService():Void
{
// Initialize the internal flag to wait.
this.wait = true;
// TODO: Find a way to force this call to wait here while allowing other calls to execute. The result is that the
// calling process cannot continue.
}
/**
* Default result handler.
* @param result object representing the result of the call.
*/
private function onResult(result:Object):Void
{
// Store the result for use.
this.result = result;
// Clear the wait flag to allow processing to continue;
this.wait = false;
// Output the details of the result;
trace("AbstractServiceConsumer: call returned with result = " + result);
}
/**
* Default error handler.
* @param status object representing the status of the call.
*/
private function onStatus(status:Object):Void
{
// Clear the wait flag to allow processing to continue;
this.wait = false;
// Output the details of the result;
throw (new Error("AbstractServiceConsumer: call returned with status = " + status));
}
}
We can then create a service. Call this service Foo.
ActionScript Code:
class Foo extends AbstractServiceConsumer
{
public function Foo()
{
// Initialize the base class with the service identifier. The base class will call a helper class called ServiceLocator that will
// take care of initializing the service instance.
super("nameOfMyRemoteService");
}
public function getSomeDataOrPerformSomeOperationThatReturnsAValue():String
{
// Delegate the call to the remote service. Note that we pass "this" as the first parameter indicating to Flash Remoting that
// we want this class instance to receive the result/status generated from the call.
super.Service.remoteMethod(this);
// Wait for the service call to send back either a result or status.
super.waitForService();
// Return the result of the operation.
return super.Result;
}
}
Now that I have a business delegate I can simply call it from my frame's action script without knowing that it is implemented using Flash Remoting.
ActionScript Code:
var foo:Foo = new Foo();
var resultOfTheCall:String = foo.getSomeDataOrPerformSomeOperationThatReturnsAValue();
From the eyes of my client using my service it is nice and clean and I can encapsulate any interactivity with Flash Remoting. That's why I need to find a way to freeze/sleep/wait/etc the specific AS line of code. Also, if you notice, you never have to deal with writing specific handlers for corresponding remote calls.
I tried using a while loop but because the flash player is essentially running in a single thread I freeze that thread and any results from the backend do not get processed until the while loop is exited. There's no way that I found to allow a yield. This would essentially allow other processes to complete pending events.
I hope the concept is clearer now, and hopefully not more complicated. Thanks for your interest.