Libspark Threading example, english - including ThreadStepProcessor
This is a basic example of how to work with the
LIBSPARK THREAD LIBRARY
In this example first a twitter api call is done and
all the avatar images of this call are beeing arranged.
Then the user will be asked to click at the stage as
an example for event handling and timeout processing.
Finally a custom process will be triggered that does
some heavy math calculation which should take
considerable amount of time on all common computers.
The loading of the images is parallel, all errors
that can occur while loading will be displayed
as small text field.
have fun! The library is cool shit!
Ps.: This is utilizing the ThreadStepProcessor extension
for the Thread library.
/**
* Copyright leichtgewicht ( http://wonderfl.net/user/leichtgewicht )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/lepP
*/
/*
This is a basic example of how to work with the
LIBSPARK THREAD LIBRARY
In this example first a twitter api call is done and
all the avatar images of this call are beeing arranged.
Then the user will be asked to click at the stage as
an example for event handling and timeout processing.
Finally a custom process will be triggered that does
some heavy math calculation which should take
considerable amount of time on all common computers.
The loading of the images is parallel, all errors
that can occur while loading will be displayed
as small text field.
have fun! The library is cool shit!
Ps.: This is utilizing the ThreadStepProcessor extension
for the Thread library.
*/
package {
import org.libspark.thread.*;
import flash.display.*;
public class ThreadLibraryExample extends Sprite {
public function ThreadLibraryExample(){
// Tell the Thread framework to use a EnterFrame
// implementation of Executor for Pseudothreads
Thread.initialize( new EnterFrameThreadExecutor() );
// Create a startup thread and start it
// -> I pass in this thread of
var setup: SetupThread = new SetupThread( this );
setup.start();
}
}
}
import flash.utils.getQualifiedClassName;
import org.libspark.thread.Thread;
import org.libspark.thread.step.StepThread;
import org.libspark.thread.threads.display.LoaderThread;
import org.libspark.thread.threads.net.URLLoaderThread;
import org.libspark.thread.utils.Progress;
import org.libspark.thread.utils.IProgress;
import org.libspark.thread.utils.events.ProgressEvent;
import org.libspark.thread.utils.ParallelExecutor;
import org.libspark.thread.utils.IProgressNotifier;
import flash.net.navigateToURL;
import flash.display.*;
import flash.errors.IOError;
import flash.events.Event;
import flash.net.URLRequest;
import flash.text.*;
import flash.system.Security;
// The Setup Thread has to extend Thread in order to do Thread
// That is a bit annoying tbh.
import flash.events.MouseEvent;
class SetupThread extends Thread {
private var _container: DisplayObjectContainer;
private var _configLoader: URLLoaderThread;
private var _allImagesLoader: ParallelExecutor;
private var _messageDisplay: MessageDisplay;
private var _pseudoThread: PseudoThread;
public function SetupThread( container: DisplayObjectContainer ) {
_container = container;
}
// Overriding the "run" template method of the Thread class
// Other events like "start" will be triggered properly
// around that
override protected function run(): void {
// Create a URLLoaderThread for the twitterfeed that offers the images
_configLoader = new URLLoaderThread( new URLRequest("http://search.twitter.com/search.atom?q=thread") );
// Configloader is started!
_configLoader.start();
// Let _configLoader block the current thread
// _configLoader is now a subthread
_configLoader.join();
// register error handlers, for both errors that occur
error( IOError, handleError );
error( SecurityError, handleError );
// After the configuration is loaded, load the images that are specified in it
next( loadImagesOfTwitterFeed );
}
// If a error occurs, show a error message!
private function handleError( error: Error, thread: Thread ): void {
message( error.message, true );
// Interrupt eventually started subthreads and stop loading following
interrupt();
}
private function loadImagesOfTwitterFeed(): void {
// If a error occured, now the thread would be interrupted!
if( !isInterrupted ) {
var xml: XML = new XML( _configLoader.loader.data );
var images: XMLList = xml..(xml.namespace())::link.(@type == "image/png" || @type == "image/jpeg" || @type == "image/jpg" || @type == "image/gif" );
// Create a new ParallelExecutor that loads the images
// beside each other
_allImagesLoader = new ParallelExecutor();
// Add all images of the xml as LoaderThread to the loader
for each( var image: XML in images ) {
_allImagesLoader.addThread( new LoaderThread( new URLRequest(image.@href) ) );
}
// Start the loading of all images
_allImagesLoader.start();
// Set this again as subthread in order to wait for the images
// to be loaded
_allImagesLoader.join();
// The error handlers from before got removed
// To listen again to the events added once more
error( IOError, handleError );
error( SecurityError, handleError );
// Next call would be to add the images to the stage
next( addImages );
}
}
private function addImages(): void {
// Could have been interrupted in last step!
if( !isInterrupted ) {
// Just for demo reasons: Draw all images
var stageWidth: int = _container.stage.stageWidth;
var maxHeight: int = 0;
var x: int = 0;
var y: int = 0;
for( var i: int = 0; i< _allImagesLoader.numThreads; ++i ) {
// Pulling the data from LoaderThread
var content: DisplayObject = LoaderThread(_allImagesLoader.getThreadAt(i)).loader;
if( x + content.width > stageWidth ) {
x = 0;
y += maxHeight;
}
content.x = x;
content.y = y;
if( content.height > maxHeight ) {
maxHeight = content.height;
}
x += content.width;
_container.addChild( content );
}
// Example for event handling
// Message is displayed
message( "click me in the next 5 seconds" );
// And once a event occurs -> The next process is triggered
event( _container.stage, MouseEvent.MOUSE_DOWN, startPseudoThread );
// Unless you clicked in 5 seconds ( 5000 ms )
wait( 5000 );
// Tell the Thread system that it should show a message
// once the timeout for wait has been exceeded
timeout( showClickFailed );
}
}
private function showClickFailed(): void {
// Wait triggered this timeout!
message( "You were too slow!", true );
}
private function startPseudoThread( event: Event = null ): void {
message( "Starting pseudo thread" );
// Initializing a pseudo thread as before
_pseudoThread = new PseudoThread();
// and starting it
_pseudoThread.start();
// waiting for it...
_pseudoThread.join();
// ... and displaying the progress
_pseudoThread.progress.addEventListener( ProgressEvent.UPDATE, showPercentage );
error( Error, handleError );
next( finish );
}
private function showPercentage( event: ProgressEvent ): void {
// Percentage is stored in Progress class from 0 to 1
message( int( _pseudoThread.progress.percent * 100) + "%" );
}
private function finish(): void {
// Wippa - the pseudo thread finished its calcuation
message( "pseudo thread finished" );
}
override protected function finalize(): void {
// Removing the references, for more controlled
// garbage cleaning after everything is done
_container = null;
_configLoader = null;
_allImagesLoader = null;
_messageDisplay = null;
}
internal function message( message: String, error: Boolean = false ): void {
if( _messageDisplay ) {
_container.removeChild( _messageDisplay );
_messageDisplay = null;
}
if( message ) {
_container.addChild( _messageDisplay = new MessageDisplay( message, error ) );
}
}
}
// Pseudo thread that counts up to the MAX constant (nothing spectacular)
class PseudoThread extends StepThread implements IProgressNotifier {
// amount to count up to
private static const MAX: int = 50000000;
private var _progress: Progress = new Progress();
private var _i: int;
override protected function init(): void {
// initial state definition
_progress.start( MAX );
_i = 0;
}
override protected function process(): Boolean {
++_i;
// return true if its completly executed
return _i >= MAX;
}
override protected function updateState(): void {
// fill the current state into progress bar
_progress.progress( _i );
}
// Implementation of IProgressNotifier method
// for the progress bar.
public function get progress(): IProgress {
return _progress;
}
}
// Just a display to show a occured error
class MessageDisplay extends Sprite {
private var _text: TextField;
public function MessageDisplay( message: String, error: Boolean ) {
addChild( _text = new TextField() );
_text.defaultTextFormat = new TextFormat( "Verdana", 10, 0xFFFFFF );
_text.autoSize = TextFieldAutoSize.LEFT;
_text.text = message;
with( graphics ) {
beginFill( error ? 0xAC0000 : 0x00AC00 );
drawRoundRect( 0, 0, _text.textWidth+10, _text.textHeight+10, 5 );
endFill();
}
addEventListener( Event.ADDED_TO_STAGE, render );
}
private function render( event: Event ): void {
x = stage.stageWidth/2 - width/2;
y = stage.stageHeight/2 - height/2;
}
}