Everybody! This is important. In a few days, these forums will be moving over to using the totally sweet Discourse platform. To ensure this migration happens smoothly with no loss of content, these forums are currently in a read-only mode. I do apologize for the inconvenience.

There is never a good time to turn the forums off for an extended period of time, but I promise the new forums will be a billion times better. I'm pretty sure of it.

See you all on the other side in a few days, and if you have any (non-technical) questions, please e-mail me at kirupa@kirupa.com. For technical questions, try to find a tutorial that corresponds to what you are looking for and post in the comments section of that page.

Cheers,
Kirupa

Results 1 to 2 of 2

Thread: some basic async stuff

  1. #1

    some basic async stuff

    So I've been working on a project for... eh... a while. Luckily I had the pleasure of working very closely with Roger Braunstein (and Jeff Yamada) for a few months... Roger is a crazy guy (http://partlyhuman.com/) in an awesome way ... lol. It was a great chance for me to learn a lot of stuff.

    Back a few months ago I was talking about having objects to act as a kind of remnant from method calls (or Objects in general) which are asynchronous. My stuff at the time was specific to loading, this stuff is much more generic.

    So at the beginning of this project we came up with some stuff to make things more consistent as we worked through the project. We knew ahead of time that we'd be dealing with a lot of the obvious stuff like loading, animating, preparing displays, but we needed to come up with a clean way to deal with the asynchronous nature of these things.

    We decided to implement a system which would allow us to observe the status of some asynchronous action through the use of asynchronous tokens. These asynchronous tokens dispatch events which specify whether or not the asynchronous action it represents completed successfully or with failure. These events are triggered in one of two ways, either the owner of the asynchronous token broadcasts an event which the token has registered as either a successful event type or a failure event type, or the owner releases the asynchronous token manually using the release method.

    The system we have in place is pretty robust so instead of presenting that I instead wrote a simpler version which I also plan on using in my own work.

    Essentially all we have is one type to represent the tokens themselves, I call this IAsyncToken and it defines the methods necessary for observing an asynchronous actions status.

    IAsyncToken
    Code:
    package com.avila.async
    {
    	import flash.events.IEventDispatcher;
    	
    	public interface IAsyncToken extends IEventDispatcher
    	{
    		function get isComplete():Boolean;
    		function get isSuccessful():Boolean;
    		
    		function release( status:String ):void;
    	}
    }
    Here you can see that asynchronous token objects are event dispatchers, this is so that they can dispatch the asynchronous token events. We also have two read-only properties which specify whether or not the asynchronous action has completed and if it has completed whether or not it was successful. We also include the method release which is passed the status of the asynchronous action and results in an asynchronous token event being dispatched.

    You can see the asynchronous token event below. This class is a simple event class which contains the two types SUCCESS and FAILURE and a property which is the asynchronous token that broadcast the event.

    AsyncTokenEvent
    Code:
    package com.avila.events
    {
    	import flash.events.Event;
    	import com.avila.async.IAsyncToken;
    
    	public class AsyncTokenEvent extends Event
    	{
    		public static const SUCCESS:String = 'async_success';
    		public static const FAILURE:String = 'async_failure';
    		
    		private var _token:IAsyncToken;
    		public function get token():IAsyncToken { return _token; }
    		
    		/**
    		 * Dispatched by an IAsyncToken to notify listeners of when it completes, and whether it was successful or not.
    		 */
    		public function AsyncTokenEvent( type:String, token:IAsyncToken, bubbles:Boolean=false, canceable:Boolean=false )
    		{
    			_token = token;
    			super ( type, bubbles, cancelable );
    		}
    	}
    }
    Let's look at a concrete implementation of an asynchronous token. Our AsyncToken class is a pretty flexible implementation. We create it by passing it the owner of the asynchronous token, and optionally the status (if it can be decided before the token is created) or event types which result in the token either succeeding or failing depending on which one the owner dispatches.

    AsyncToken
    Code:
    package com.avila.async
    {
    	import flash.events.EventDispatcher;
    	import flash.events.Event;
    	import flash.events.IEventDispatcher;
    	import com.avila.events.AsyncTokenEvent;
    	import flash.events.ErrorEvent;
    
    	public class AsyncToken extends EventDispatcher implements IAsyncToken
    	{
    		public static const SUCCESS:String = "success";
    		public static const FAILURE:String = "failure";
    		
    		private var _successfulEventTypes:Array = new Array();
    		// events that will result in the async token completing successfully
    		public function get successfulEventTypes():Array { return _successfulEventTypes.slice(0); }
    		
    		private var _failureEventTypes:Array = new Array();
    		// events that will result in the async token completing with a failure
    		public function get failureEventTypes():Array { return _failureEventTypes.slice(0); }
    		
    		private var _isComplete:Boolean = false;
    		// Whether or not the token has completed.
    		public function get isComplete():Boolean { return _isComplete; }
    		
    		private var _isSuccessful:Boolean;
    		// Whether or not the token was successful, if isComplete is false, this will be null.
    		public function get isSuccessful():Boolean { return _isSuccessful; }
    		
    		private var _owner:Object;
    		// The object which owns this async token
    		public function get owner():Object { return _owner; }
    		
    		/**
    		 * Creates a new AsyncToken.
    		 * 
    		 * @param owner The object which dictates when this async token has completed.
    		 * @param status The status of this AsyncToken if it can be decided before the token is created
    		 * 				 Leave this argument null unless you want the asynctoken to automatically be completed.
    		 * @param successfulEventTypes The event types which result in the token being successfully completed.
    		 * @param failureEventTypes The event types which result in the token failing.
    		 * 
    		 * If the status argument is set the token will not dispatch any events, so if you think a token may have had
    		 * it set during creation, then check it's IAsyncToken.isComplete property once you receive the token.
    		 */
    		public function AsyncToken( owner:Object, status:String=null, successfulEventTypes:Array=null, failureEventTypes:Array=null )
    		{
    			_owner = owner;
    			
    			// if success/failure event types were passed  work with them
    			if ( successfulEventTypes )
    				_successfulEventTypes = successfulEventTypes.slice(0);
    			
    			if ( failureEventTypes )
    				_failureEventTypes = failureEventTypes.slice(0);
    				
    			configureListeners();
    			
    			if ( status )
    				release( status );
    		}
    		
    		/**
    		 * Releases the AsyncToken with the status of either AsyncToken.SUCCESS or AsyncToken.FAILURE
    		 */
    		public function release( status:String ):void
    		{
    			// release the token manually
    			if ( status == SUCCESS )
    				onOwnerSuccess( null );
    			else if ( status == FAILURE )
    				onOwnerFailure( null );
    		}
    		
    		/**
    		 * The finalize method is what cleans up the token so that there are no references
    		 * between it and it's owner.
    		 */
    		public function finalize():void
    		{
    			var ownerDispatcher:IEventDispatcher = owner as IEventDispatcher;
    			
    			if ( !ownerDispatcher )
    				return;
    				
    			// listen for all of the events
    			var count:uint = Math.max( _successfulEventTypes.length, _failureEventTypes.length );
    			while ( count-- )
    			{
    				if ( count < _successfulEventTypes.length )
    					ownerDispatcher.removeEventListener( _successfulEventTypes[ count ], onOwnerSuccess );
    					
    				if ( count < _failureEventTypes.length )
    					ownerDispatcher.removeEventListener( _failureEventTypes[ count ], onOwnerFailure );
    			}
    			
    			_owner = null;
    		}
    
    		// Executes when is successful with what it was doing
    		private function onOwnerSuccess( event:Event ):void
    		{
    			_isComplete = true;
    			_isSuccessful = true;
    			
    			dispatchEvent( new AsyncTokenEvent( AsyncTokenEvent.SUCCESS, this ) );
    		}
    		
    		// Executes when the owner fails what it's doing
    		private function onOwnerFailure( event:Event ):void
    		{
    			_isComplete = true;
    			_isSuccessful = false;
    			
    			dispatchEvent( new AsyncTokenEvent( AsyncTokenEvent.FAILURE, this ) );
    		}
    		
    		// If success/failure event types were passed and the owner is an IEventDispatcher
    		// then listen for those events
    		private function configureListeners():void
    		{
    			var ownerDispatcher:IEventDispatcher = owner as IEventDispatcher;
    			
    			if ( !ownerDispatcher )
    				return;
    				
    			// we listen to two events by default Event.COMPLETE and ErrorEvent.ERROR
    			if ( _successfulEventTypes.indexOf( Event.COMPLETE ) == -1 )
    				_successfulEventTypes.push( Event.COMPLETE );
    				
    			if ( _failureEventTypes.indexOf( ErrorEvent.ERROR ) == -1 )
    				_failureEventTypes.push( ErrorEvent.ERROR );
    				
    			// listen for all of the events
    			var count:uint = Math.max( _successfulEventTypes.length, _failureEventTypes.length );
    			while ( count-- )
    			{
    				if ( count < _successfulEventTypes.length )
    					ownerDispatcher.addEventListener( _successfulEventTypes[ count ], onOwnerSuccess );
    					
    				if ( count < _failureEventTypes.length )
    					ownerDispatcher.addEventListener( _failureEventTypes[ count ], onOwnerFailure );
    			}
    		}
    	}
    }
    When you create an AsyncToken you can pass it a status argument. This argument's value can be either AsyncToken.SUCCESS or AsyncToken.FAILURE. If this property is set then the release method gets called at the last line of the constructor... now... I'm not sure that I like this, but what it means is that if your token can have it's status decided before it is created then you need to be sure to check the isComplete property of that token when you receive it. Usually a call to release() is going to result in dispatching an asynchronous token event, which you'd usually listen to. However, if the token dispatches an event during creation then there is pretty much no way to listen for it.

    You can see a pretty useless demo of this with the following Main file.

    Main
    Code:
    package
    {
    	import flash.display.Sprite;
    	import com.avila.async.IAsyncToken;
    	import com.avila.async.AsyncToken;
    	import com.avila.events.AsyncTokenEvent;
    	import flash.net.URLLoader;
    	import flash.net.URLRequest;
    	import flash.events.Event;
    
    	public class Main extends Sprite
    	{
    		private var loader:URLLoader;
    		
    		public function Main()
    		{
    			loader = new URLLoader();
    		
    			var token:IAsyncToken = new AsyncToken( loader, null, [ Event.COMPLETE ] );
    			token.addEventListener( AsyncTokenEvent.SUCCESS, onLoadSuccess );
    			
    			// token.release( AsyncToken.SUCCESS );
    			loader.load( new URLRequest( "http://www.google.com" ) );
    		}
    		
    		private function onLoadSuccess( event:AsyncTokenEvent ):void
    		{
    			trace( "load complete" );
    			trace( "load data: " + loader.data );
    		}
    	}
    }
    Okay, so I can go on about this a little more if you guys want me to. There's also room for things like CompositeTokens as well as discussing how to chain asynchronous actions together.

    I don't know, what do you guys think? I can go from there.

    -M

  2. #2
    The other option for release (as it is implemented in our more robust version) is to wait 1 millisecond after the object has been created and then release the object. This is probably the better, cooler, cleaner way to go, but what do you guys think?

    -M

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

Home About kirupa.com Meet the Moderators Advertise

 Link to Us

 Credits

Copyright 1999 - 2012