Creating Responder Functions
You’ve seen different ways of creating a responder function for a remote service call. There are two broad categories of responder functions:
The responder functions can be methods named onResult( ) and onStatus( ) (or, more accurately, they are functions assigned to the
onResult
andonStatus
properties of a responder object).Responder functions can also be named functions, in which case the function name must match the name of the remote method followed by _Result or _Status, such as
methodname
_
Result( ) andmethodname
_
Status( ). This approach is used in some of the Macromedia documentation and in Example 3-8.
Now that you understand the basics, here is the twist. A responder object can be passed to getService( ), in which case the same responder object is used for all future method invocations on that service. Alternatively, a responder object can be passed separately each time a method is invoked on the service, in which case a responder object should not be passed in the initial call to getService( ).
Using onResult( ) and onStatus( ) Responder Functions
Let’s first look at passing a responder object to
getService( ). Recall the syntax for calling the
getService( ) method, where
myConnection_conn
is a
NetConnection object returned by an earlier call
to createGatewayConnection( ):
myService =myConnection_conn
.getService(serviceName
[,responderObject
]);
The first parameter, serviceName
, is a
service name such as com.oreilly.frdg.HelloWorld
.
The optional second parameter,
responderObject
, is any object that
defines onResult(
)
and onStatus( )
methods,
which will handle responses from
future calls to methods of the remote service. The Macromedia
documentation sometimes refers to
responderObject
as a
result-handler callback object, we use the term
responder object.
Note
The responderObject
argument does not
receive the result of the getService( ) call
itself, which we stored in myService
. Instead,
responderObject
is used to obtain the
results from subsequent calls to methods of the service. The
myService
object is simply a proxy through which
you can make calls to methods of the remote service.
Many examples from Macromedia and elsewhere use the keyword
this
as the responder object, which causes Flash
to look for callback functions defined on the object from which the
getService( ) method is invoked. The only
requirements for a responder object is that it defines an
onResult( ) and onStatus( )
method, or uses named callback functions as described later in this
section.
The responder object can take different forms. In this excerpt from Example 1-1, a generic instance of the Object class was created to handle the response from remote method calls:
myResult = new Object( ); myResult.onResult = function (data) { trace("Data received from Server : " + data); }; myResult.onStatus = function (info) { trace("An error occurred : " + info.description); }; // ...other code omitted var myService = myServer.getService(myServicePath, myResult);
Note how the myResult
object is passed as the
responderObject
parameter of the
getService( ) method. After a remote method call
on the service completes, the onResult( ) method
of the myResult
object will receive the results
(unless an error occurs, in which case the onStatus(
) method will be invoked instead).
The following example defines the onResult( )
and onStatus( ) handlers on the current Flash
object, as specified by the keyword this
, and
passes this
as the responder object. This
technique is also commonly seen throughout this book and in
Macromedia’s documentation:
this.onResult = function (myResult) { results_txt.text = myResult; }; this.onStatus = function (myError) { results_txt.text = myError.description; }; // Setting up ofmyConnection_conn
andservicePath
variables are not shown var myService = myConnection_conn.getService(servicePath, this);
Recall that you can invoke any service function as a method of the service object returned by getService( ):
// Call a service function namedfunctionA( )
onmyService
with no parameters. myService.functionA( );
If the service function expects parameters, you can pass the parameters to it just as with any other method invocation:
// Call a service function namedfunctionB( )
onmyService
with two parameters. myService.functionB("sample1", "sample2");
If you passed a responderObject
parameter
when calling getService( ) earlier, the response
from each remote method call is passed to the responder
object’s onResult( ) handler.
However, if you didn’t specify a responder object when calling the getService( ) method, you can specify named callback functions for each remote method called (as discussed later).
First, let’s discuss another option: passing a responder object as the first argument when invoking a remote function on the service. If the first argument is an object defining an onResult( ) method, the NetServiceProxyResponder object strips it from the argument list passed to the remote service function and uses it as a responder object instead (the responder object parameter is not sent to the service function).
For example:
// Create the service object without specifying a responder object. myService = myConnection.getService("serviceName"); // CallfunctionA( )
, specifying thatmyResponseObjectA
should handle the // results, but without passing any additional parameters. // The definition ofmyResponseObjectA
is not shown. //myResponseObjectA
is not sent to the service function. myService.functionA(myResponseObjectA); // CallfunctionB( )
, specifying thatmyResponseObjectB
should handle // the results, and pass two additional parameters.myResponseObjectB
is not // sent to the service function, but the two string parameters are sent. // The definition ofmyResponseObjectB
is not shown. myService.functionB(myResponseObjectB, "sample1", "sample2");
Specifying the responder object when invoking a remote method on the service lets you specify different responder objects for each remote method call, as shown in the preceding example. You don’t have this flexibility if the responder object is set when calling getService( ). If you set a responder object via getService( ) and attempt to specify another responder object when invoking a remote function, it won’t work. The responder object will be passed as a parameter to the remote function instead of being stripped out of the argument list.
Using Named Responder Functions
An alternative to using onResult( ) and onStatus( ) responder functions is to use named responder functions that match the name of the method. For example, here we define two named responder functions for the sayHello( ) method:
function sayHello_Result (myResult) { results_txt.text = myResult; } function sayHello_Status (myError) { results_txt.text = myError.description; }
When a remote service call returns a result, the
NetServiceProxyResponder object, which handles
the result from the remote call, looks for a function that follows
the methodName
_Result(
) naming convention. Thus, onResult
events generated by the sayHello( ) function
cause Flash Remoting to invoke the sayHello_Result(
) function. Similarly, error events generated by the
sayHello( ) function cause Flash Remoting to
invoke the sayHello_Status( ) function
Using named functions in this way keeps the result and status callback functions separate for each remote method call. Contrast this with the approach in which the onResult( ) and onStatus( ) handlers of a responder object passed to getService( ) handle the results of all remote method calls on that service.
Response Dispatch Hierarchy
Now that we know about the various ways that responder objects and functions can be specified, how does Flash Remoting decide which responder function to invoke when results are returned from a remote method call?
We saw earlier that when a service is established via getService( ), Flash generates a NetServiceProxyResponder object. When a remote method call returns a result, a corresponding onResult (or onStatus) event is serialized by the Flash Remoting gateway as part of the AMF packet that is sent back to your Flash movie.
The NetServiceProxyResponder object dispatches the onResult event from a remote call in this order:
First, it looks for a function that is named using the
methodname
_Result( ) convention. If it finds one, results are sent to that function. This function can be defined on the responder object or the current timeline.If the
methodname
_Result( ) function isn’t found and a responder object with an onResult( ) method was specified in the call to getService( ), results are sent to that responder object’s onResult( ) method.If a responder object wasn’t specified in the call to getService( ) and the first argument passed to the remote method invocation is an object that defines an onResult( ) method, the first argument is assumed to be a responder object and results are sent to its onResult( ) method.
If no responder object is specified (or if the specified responder object lacks an onResult( ) method), the NetServiceProxyResponder object sends the results to the Output window if the movie is playing in the authoring environment. Otherwise, the results are lost.
The NetServiceProxyResponder object also handles the onStatus event of the remote service in this order:
First, it looks for a function that is named using the
methodname
_Status( ) convention. If it finds one, status errors are sent to that function.If the
methodname
_Status( ) function isn’t found and a responder object with an onStatus( ) method was specified, results are sent to the responder object’s onStatus( ) method.If no responder object is specified (or if the specified responder object lacks an onStatus( ) method), the
_root
level is checked for an onStatus( ) method. If it is found, it is used.If that is not found, the _global.System.onStatus( ) method, if any, is used.
Finally, if none of the preceding handlers are found, the NetServiceProxyResponder object sends the status to the Output window in the authoring environment. Otherwise, the status is lost.
In the authoring environment, if you don’t specify responders, the results are displayed in the Output window. This can be handy when testing applications.
Choosing the Appropriate Type of Responder Function
Now that you understand your options, which type of responder function should you use? The answer depends on your application’s structure and requirements.
Named result functions are typically used when you have specified a default responder object for a service object (that is, when you’ve passed a responder object to the getService( ) method). This technique allows a single responder object to define separate responder functions for each remote service function (because of the naming convention used).
You should use onResult( ) and onStatus( ) responder functions when you are passing a responder object as the first parameter to each service function invocation. This technique is quite flexible: you can define different responder objects for each service function invocation, or you can share a single responder object among multiple service function invocations.
Provided you understand the mechanisms, you can mix and match the techniques to suit your situation. Now we will we explore various possible situations and solutions.
By passing a responder object to getService( ), you can use one event handler to handle all the results or errors for multiple remote method calls, if appropriate. For example, if you have a service that accessed a company employees database, you might have various methods like this:
myService.addEmployee(name
); myService.deleteEmployee(ID
); myService.updateEmployee(ID
,record
);
Each method can return true
if it is a successful
database transaction. If you use named functions to handle the
results, each of these remote method calls needs its own set of
responder functions, as in this code snippet:
updateEmployee_Result (result) { if (result != true) results_txt.text = "There was an error."; } deleteEmployee_Result (result) { if (result != true) results_txt.text = "There was an error."; } addEmployee_Result (result) { if (result != true) results_txt.text = "There was an error."; } updateEmployee_Status (status) { results_txt.text = status.description; } deleteEmployee_Status (status) { results_txt.text = status.description; } addEmployee_Status (status) { results_txt.text = status.description; }
Using the responder object approach, this example could be written using one onResult( ) handler and one onStatus( ) handler attached to a generic object:
Responder = new Object( ); Responder.onResult = function (result) { if (result != true) results_txt.text = "There was an error."; }; Responder.onStatus = function (status) ( results_txt.text = status.description; };
Or, if you pass this
(i.e., the current object) as
the responder object, you can simply write:
onResult = function (result) { if (result != true) results_txt.text = "There was an error."; }; onStatus = function (status) ( results_txt.text = status.description; };
Using a responder object is more concise in this particular case. In addition, it is in keeping with object-oriented design. The named handler functions are easy to comprehend and use, but they are more typical of procedural programming.
However, you may need to process the results of each remote method call differently. For example, suppose the addEmployee( ), deleteEmployee( ), and updateEmployee( ) methods each require special handling. In such cases, you can pass a responder object as the first argument in the remote method call, as described earlier under Section 4.3.1.
Applying this technique to the hypothetical addEmployee( ), deleteEmployee( ), updateEmployee( ) methods, the resulting ActionScript might look like Example 4-1.
#include "NetServices.as" // Set up variables for the URL and service paths. var myURL = "http://localhost/flashservices/gateway"; var servicePath = "com.oreilly.frdg.SampleDatabaseMethods"; // Define the custom responder class for the remoteupdateEmployee( )
method. function UpdateResult ( ) { } // Define a customonResult( )
handler for theUpdateResult
class. UpdateResult.prototype.onResult = function (myResults) { results_txt.text = "Update employee successful"; // Do some housekeeping after updating an employee }; UpdateResult.prototype.onStatus = errorHandler; // Define the custom responder class for the remoteaddEmployee( )
method. function AddResult ( ) { } // Define a customonResult( )
handler for theAddResult
class. AddResult.prototype.onResult = function (myResults) { results_txt.text = "Add employee successful"; // Do some housekeeping after adding an employee }; //AddResult
and subsequent classes all share a single error handler. AddResult.prototype.onStatus = errorHandler; // Define the custom responder class for the remotedeleteEmployee( )
method. function DeleteResult ( ) { } // Define a customonResult( )
handler for theDeleteResult
class. DeleteResult.prototype.onResult = function (myResults) { results_txt.text = "Delete employee successful"; // Do some housekeeping after deleting an employee }; DeleteResult.prototype.onStatus = errorHandler; System.onStatus = errorHandler; function errorHandler (myError) { results_txt.text = myError.description; } // Connection hasn't been initialized; create connection and service objects. if (initialized == null) { initialized = true; NetServices.setDefaultGatewayURL(myURL); var myConnection_conn = NetServices.createGatewayConnection( ); var myService = myConnection_conn.getService(servicePath); } // Set up the callback functions to handle mouseclicks. add_pb.setClickHandler("callAdd"); update_pb.setClickHandler("callUpdate"); delete_pb.setClickHandler("callDelete"); // Call the remote service when the user clicks the buttons. function callAdd ( ) { myService.addEmployee(new AddResult( ), "Jack O'Lantern"); } function callUpdate ( ) { myService.updateEmployee(new UpdateResult( ), myRecordNum, myRecord); } function callDelete ( ) { myService.deleteEmployee(new DeleteResult( ), myRecordNum); }
Each remote method call has a corresponding responder object that defines a custom onResult( ) handler. Notice, however, that all responder objects share a common error handler function. This allows you to process the results of each remote method differently while economizing with a single error handler.
We’ve seen how to invoke different responder functions for different remote methods, but you may want to distinguish between multiple calls to the same remote method. Remember that remote method invocations are asynchronous, and you cannot rely on results being returned to Flash in the same order in which the functions are invoked. Therefore, if you are using the same responder function for multiple calls to the same remote service function, you can’t tell which service function invocation returned a particular result. To distinguish between the results from multiple calls to the same remote method, you can use a separate instance of a custom class for each responder object. Attach a custom property to each responder object instance and check its value when the result is returned to the responder function.
This solution adds an id
parameter to the
AddResult class constructor. You can create
multiple instances of the AddResult
class—one for each function invocation—and assign each
one a unique id
. Then, you can distinguish between
results using the id
property of the responder
object. Replace the following functions in Example 4-1 with these new versions:
// Define the custom responder class for the remoteaddEmployee( )
method. // Assign anid
property to each instance. function AddResult (id) { this.id = id; } // Define a customonResult( )
handler for theAddResult
class. AddResult.prototype.onResult = function (myResults) { // Process the result differently, depending on the value of theid
property. results_txt.text = "Employee " + this.id + " added successfully"; };
Now you can invoke the same service function multiple times. In each
case, use an instance of the AddResult class as
the responder object, but assign each instance an
id
corresponding to the employee name so
that you can distinguish between them when the results are returned.
myService.addEmployee(new AddResult("Jack Sprat"), "Jack Sprat"); myService.addEmployee(new AddResult("Jack Beanstalk"), "Jack Beanstalk"); myService.addEmployee(new AddResult("Jack O'Lantern"), "Jack O'Lantern");
Let’s look at one more scenario for creating and managing responder objects. You can create a common responder class (named BaseResult in the following code snippet) and then create new responder objects that inherit from the base class for each remote method:
// Define a BaseResult
class. This class is never called directly,
// but it acts as a base class for responder objects.
function BaseResult ( ) { }
BaseResult.prototype.onResult = function (myResults) {
trace("success");
};
BaseResult.prototype.onStatus = function (myError) {
results_txt.text = myError.description;
};
system.onStatus = BaseResult.prototype.onStatus;
UpdateResult.prototype = new BaseResult( );
function UpdateResult( ) { // Empty constructor
}
UpdateResult.prototype.onResult = function (myResults) {
results_txt.text = "Update employee successful";
// Do some housekeeping after updating an employee.
};
// etc.
In this scenario, the onStatus( ) handler is defined in the parent class (BaseResult) and is available to all of the classes that inherit from it. Each class that is created implements its own onResult( ) method. The full code listing for this example is available at the online Code Depot as SampleDatabaseMethods2.fla.
You can also establish a hierarchy of result handlers in your Flash movie. You can do this if you have several methods that can share a result handler but some methods that need special handling. For example, the following script defines two dedicated result handlers and one generic handler that will handle all other remote method calls:
function myMethod1_Result (result) { // Do some stuff formyMethod1( )
} function myMethod2_Result result) { // Do some stuff formyMethod2( )
} function onResult (result) { // Do some generic stuff for all other methods }
The first two functions correspond to method names, and the third function simply acts as a generic method that all other remote method calls will use as a result handler.
Of course, the bottom line is that these techniques are all available and you should use what you feel comfortable with or what the situation demands. Section 12.7.2.4 shows other techniques for responder objects that rely on callback functions or broadcasters to handle results more elegantly.
Get Flash Remoting: The Definitive Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.