TimedForEach Class

I ran into some trouble today with batch processing some files.
I am processing a set of files, but it is possible that some of the supplied files are invalid.
In that case i want to be able to pop up a messagebox and ask the user if he wants to retry, ignore or abort.
A lot of the involved calls have to be processed asynchronously and i have to give control back to the runtime in between calls, so that Flash can update the display (amongst other things).

The obvious solution is a queue that pauses in between calls to the processing routine and that can be paused while waiting for the user input.

This class does exactly that.

/**
* ...
* @author Oliver "gencha" Salzburg
* @version 0.1
*/

package util {
	import flash.events.EventDispatcher;
	import flash.utils.Timer;
	import flash.events.TimerEvent;

	/**
	 * The TimedForEach class enabled you to run a command on an array of items while having full control over the process.
	 * The class allows for processing of the items in an asychronous environment.
	 * You can pause the loop and resume it later on.
	 * A typical application would be pausing the loop while waiting for user input which could manipulate the way the loop behaves.
	 */
	public class TimedForEach extends EventDispatcher {

		/**
		 * The delay between calls to the item processor
		 */
		private static const DEFAULT_DELAY:uint = 100;

		private var targets_:Array;
		private var command_:Function;

		private var targetIndex_:int;
		private var lastIndex_:int;

		private var timer_:Timer;

		private var isStalled_:Boolean;

		/**
		 * Default constructor
		 * @param	targets The array that is to be processed.
		 * The array is not copied and not modified by the class
		 * @param	command The command that is applied to each item in the targes array.
		 * The function is called with a single target as it's parameter
		 */
		public function TimedForEach( targets:Array, command:Function ) {
			targets_ = targets;
			command_ = command;
		}

		/**
		 * Start the loop
		 */
		public function execute():void {
			isStalled_ 		= false;
			targetIndex_ 	= 0;
			lastIndex_		= 0;

			timer_ = new Timer( DEFAULT_DELAY );
			timer_.addEventListener( TimerEvent.TIMER, checkState );
			timer_.start();
		}

		/**
		 * Stop processing
		 */
		public function kill():void {
			timer_.stop();
		}

		/**
		 * Process the next item if possible
		 * @param	event
		 */
		private function checkState( event:TimerEvent ):void {
			if( isStalled_ ) return;

			if( targets_.length <= targetIndex_ ) {
				dispatchEvent( new TimerEvent( TimerEvent.TIMER_COMPLETE ) );
				kill();
				return;
			}

			lastIndex_ = targetIndex_;
			command_( targets_[ targetIndex_++ ] );
		}

		/**
		 * Pause processing
		 * @see #resume()
		 */
		public function stall():void {
			isStalled_ = true;
		}

		/**
		 * Resume processing
		 * @see #stall()
		 */
		public function resume():void {
			isStalled_ = false;
		}

		/**
		 * Retry the last processed item
		 */
		public function again():void {
			targetIndex_ = lastIndex_;
		}

		/**
		 * Resets the loop on the first item in the list
		 */
		public function reset():void {
			targetIndex_ 	= 0;
			lastIndex_ 		= 0;
		}

	 	/**
		 * Returns an exact copy of the object
		 * @return An exact copy of the object
		 */
		public function clone():TimedForEach {
			var clone:TimedForEach = new TimedForEach( targets_, command_ );
			return clone;
		}

		/**
		 * Returns a representation of the object as a string
		 * @return A representation of the object as a string
		 */
		override public function toString():String {
			return "[object TimedForEach]";
		}

	}

}

Leave a Reply

You must be logged in to post a comment.