Timeout appears to be about 10 seconds. Can this be modified?
I don't see a built-in way to do it. However, it's definitely possible to implement on your own.
Just create a transaction class representing each call you make to a NetConnection instance.
This transaction class, for example "NetTransaction", should keep a private static list of all active transactions, and should store the result handler function in a private instance variable that is to be called when a transaction completes (on result, on status, or on timeout). Note that this handler is unified, so it handles all kinds of results (success/error/timeout/canceled).
In your transaction class's constructor, add "this" new instance to the active transactions list, start a timeout timer if a non-zero timeout is specified (adding an event listener pointing to the cancelTransaction function described below), and then perform the network call last.
When you complete a transaction (success/error/timeout/canceled), remove it from the active transactions list, cancel the timeout timer if one was set, and finally forward a meaningful result to the result handler function.
The trick to making this all work is that you must create result and status handler functions in the transaction class, and pass THOSE to your call to NetConnection. Those two functions will be responsible for INTERCEPTING the network result (result or status), completing the transaction (as described above), and forwarding a unified result to the REAL result handler function that was passed to the constructor.
Here's the stripped down insides of the base NetTransaction class with the basic necessities. My implementation has more stuff, including generating transaction ids (just a simple static counter, a method for looking up an active transaction by id number. It also has overridable get/set methods for the transaction's data object, so I can have automatic header wrapping/unwrapping for custom data protocols in classes deriving from NetTransaction (e.g. WidgetNetTransaction).
static private var active_transactions:Array = new Array(); //active network requests pending a result
static private var transaction_count:int = 0; //incremented each time a NetTransaction instance is created so each one can have a unique transaction id number assigned to it
private var transaction_id:int; //Transaction identifier, which may assist a widget in managing its own concurrent transactions. It comes from a static field, auto-incremented in the NetTransaction constructor, so it is always unique for each NetTransaction within the current session... unless more than 2147483648 transactions occur in a single session and the value wraps around, but by then, old transactions wil be forgotten and there shouldn't be any problems as a result.
private var description:String; //an optional description string to describe the transaction or what it is supposed to do (especially for error-reporting purposes).
private var request_data:Object; //this stores the data that will be forwarded to your web server
private var result_handler:Function; //this is the method to be called after intercepting a result or status event. it's left public, because it's acceptable to modifiy it mid-transaction, although I can't think of a good reason to ever do so
private var internal_responder:Responder; //internal responder attached to the transaction
private var timeout:int;
private var timeout_timer:Timer;
//Constructor
public function NetTransaction( network_service:NetworkService, request_data:Object, result_handler:Function = null, description:String = null, timeout:int = 0 )
{
//Throw something a little more friendly than a null-reference error.
if (network_service == null)
throw new ArgumentError( "A NetworkService object must be specified for all NetTransaction objects." );
if (timeout < 0)
throw new ArgumentError( "Timeout must be 0 (infinite) or greater to specify the number of milliseconds after which the transaction should be cancelled.\rBe sure to give the transaction enough time to complete normally." );
//Save information related to the transaction
this.result_handler = result_handler;
this.request_data = request_data;
this.internal_responder = new Responder( net_result, net_status ); //should use override versions of these methods
this.description = description;
this.timeout = timeout;
this.timeout_timer = null;
//Grab a new transaction id, add the transaction to the list of active transactions, set up a timeout timer, and finally call the service method.
this.transaction_id = transaction_count++;
active_transactions.push( this ); //transaction is now registered; this is done BEFORE setting the timeout, and before actually sending it out on the network
if (timeout > 0) //zero, represents an infinite timeout, so we'll only create and start a timer if there is a non-zero timeout specified
{
timeout_timer = new Timer( timeout, 1 );
timeout_timer.addEventListener( TimerEvent.TIMER, this.cancelTransaction, false, 0, true );
timeout_timer.start();
}
network_service.call( internal_responder, request_data );
}
//Finalizes a transaction by removing it from the active transactions list, and returns true.
//Returns false to indicate that the transaction was already complete, and was not found in the active transactions list.
private function completeTransaction():Boolean
{
var index:int = active_transactions.indexOf( this );
if (index > -1)
{
active_transactions.splice( index, 1 );
if (timeout_timer != null)
{
timeout_timer.stop();
timeout_timer.removeEventListener( TimerEvent.TIMER, this.cancelTransaction, false );
}
return true;
}
else
{
//Transaction being removed was already completed or was cancelled
return false;
}
}
//An instance version of the static NetTransaction.cancelTransaction function, which automatically passes the transaction instance.
public function cancelTransaction( details_status_object:Object = null )
{
NetTransaction.cancelTransaction( this, details_status_object );
}
//Cancels all active transactions immediately, forcing all pending transactions to complete immediately with a "NetTransaction.Call.Cancelled" status code.
static public function cancelAllActiveTransactions( details_status_object:Object )
{
for each (var transaction:NetTransaction in active_transactions)
transaction.cancelTransaction( details_status_object );
}
//Cancels the transaction by spoofing an error result object to the net_status callback.
static public function cancelTransaction( transaction:NetTransaction, details_status_object:Object )
{
//Build cancel event status object, containing somewhat standard properties [code,level,description,details,type].
var status:NetTransactionResultStatus = new NetTransactionResultStatus(
"NetTransaction.Call.Cancelled",
"error", //cancelling a transaction makes it incomplete, so the status level should be "error"
"A network transaction was cancelled. The description given for the transaction was: " + transaction.description,
details_status_object, //include, in the details, the status object passed to this method
"" //no type specified
);
//Call the net_status handler directly, passing a dynamic Object-typed version of the NetTransactionResultStatus to the net_status handler.
transaction.net_status( status.ToObject() );
}
//Result responder. Override, then call when you're ready to respond to pre-process the object returned to the result_handler.
protected function net_result( result_object:Object ):void
{
if (completeTransaction())
if (result_handler != null)
result_handler.call( null, new NetTransactionResult( this, result_object, null ) );
}
//Status responder. Override, then call when you're ready to respond to pre-process the object returned to the result_handler.
protected function net_status( status_object:Object ):void
{
if (completeTransaction())
if (result_handler != null)
result_handler.call( null, new NetTransactionResult( this, null, NetTransactionResultStatus.FromObject( status_object ) ) );
}
The NetTransactionResult and NetTransactionResultStatus classes are just simple data classes I've set up for type-safe results to be sent to a unified result handler function. It interprets results as an error if the NetTransactionResult has a non-null status property, otherwise, it interprets the result as a success and uses the included data. The NetworkService class you see is just a wrapper around the NetConnection class that handles specifying the call path, and also handles all the low-level NetConnection error events, packages status messages compatible with the NetTransaction class, and finally calls cancelAllTransactions.
The beauty of this setup is that now no matter what kind of error happens, including timeouts, your result handler for a transaction will ALWAYS be called, with all the information you need to handle the result (success/error/timeout/canceled). This makes using the NetConnection object almost as simple and reliable as calling a local function!