flash.system

# Worker - AS3

Package | flash.system
Class | public final class Worker
Inheritance | Worker EventDispatcher Object

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

A Worker object represents a worker, which is a virtual instance of the Flash runtime. Each Worker instance controls and provides access to the lifecycle and shared data of a single worker.

A worker allows you to execute code "in the background" at the same time that other operations are running in another worker (including the main swf's worker). In a non-worker context some operations, for example processing a large set of data in a loop, take so long to execute that they prevent the main application thread from updating the screen quickly enough. This can cause stuttering or freezing the screen.

Using a worker allows you to perform a long-running or slow operation in the background. Each worker runs its code in a separate thread of execution from other workers. Long-running code in one worker does not block code in another worker from executing. Instead, the two sets of code run in parallel. Consequently, a worker can be used to execute code in the background while the main application thread stays free to continue updating the screen.

This capability of simultaneously executing multiple sets of code instructions is known as concurrency.

Note: The use of workers for concurrency is supported in both Flash Player and AIR on desktop platforms. For mobile platforms, concurrency is supported in AIR on both Android and iOS. You can use the static isSupported property to check whether concurrency is supported before attempting to use it.

You do not create Worker instances directly by calling the `Worker()` constructor. In contexts where the use of workers for concurrency is supported, at startup the runtime automatically creates the Worker associated with the main SWF, known as the primordial worker.

Each additional worker is created from a separate swf. To create a new instance of the Worker class, pass a ByteArray with the bytes of the background worker's swf as an argument to the WorkerDomain class's `createWorker()`method. There are three common ways to access the bytes of a swf for this purpose:

- Use the [Embed] metatag to embed the .swf file in the application as a ByteArray:

  Copy

       // Embed the SWF file
       [Embed(source="../swfs/BgWorker.swf", mimeType="application/octet-stream")]
       private static var BgWorker_ByteClass:Class;

       private function createWorker():void
       {
         // create the background worker
         var workerBytes:ByteArray = new BgWorker_ByteClass();
         var bgWorker:Worker = WorkerDomain.current.createWorker(workerBytes);

         // listen for worker state changes to know when the worker is running
         bgWorker.addEventListener(Event.WORKER_STATE, workerStateHandler);

         // set up communication between workers using
         // setSharedProperty(), createMessageChannel(), etc.
         // ... (not shown)

         bgWorker.start();
       }

- Load an external SWF file using a URLLoader:

  Copy

       // load the SWF file
       var workerLoader:URLLoader = new URLLoader();
       workerLoader.dataFormat = URLLoaderDataFormat.BINARY;
       workerLoader.addEventListener(Event.COMPLETE, loadComplete);
       workerLoader.load(new URLRequest("BgWorker.swf"));

       private function loadComplete(event:Event):void
       {
         // create the background worker
         var workerBytes:ByteArray = event.target.data as ByteArray;
         var bgWorker:Worker = WorkerDomain.current.createWorker(workerBytes);

         // listen for worker state changes to know when the worker is running
         bgWorker.addEventListener(Event.WORKER_STATE, workerStateHandler);

         // set up communication between workers using
         // setSharedProperty(), createMessageChannel(), etc.
         // ... (not shown)

         bgWorker.start();
       }

- Use a single swf as both the primordial worker and the background worker:

  Copy

       // The primordial worker's main class constructor
       public function PrimordialWorkerClass()
       {
         init();
       }

       private function init():void
       {
         var swfBytes:ByteArray = this.loaderInfo.bytes;

         // Check to see if this is the primordial worker
         if (Worker.current.isPrimordial)
         {
           // create a background worker
           var bgWorker:Worker = WorkerDomain.current.createWorker(swfBytes);

           // listen for worker state changes to know when the worker is running
           bgWorker.addEventListener(Event.WORKER_STATE, workerStateHandler);

           // set up communication between workers using
           // setSharedProperty(), createMessageChannel(), etc.
           // ... (not shown)

           bgWorker.start();
         }
         else // entry point for the background worker
         {
           // set up communication between workers using getSharedProperty()
           // ... (not shown)

           // start the background work
         }
       }

Workers execute in isolation from each other and do not have access to the same memory, variables, and code. However, there are three mechanisms available for passing messages and data between Worker instances:

- Shared properties: Each worker has an internal set of named values that can be set and read both from within the worker itself as well as from other workers. You can set a value using the `setSharedProperty()` method and read a value using the `getSharedProperty()` method.
- MessageChannel: A MessageChannel object allows you to send one-way messages and data from one worker to another. Code in the receiving worker can listen for an event to be notified when a message arrives. To create a MessageChannel object, use the `createMessageChannel()` method.
- Shareable ByteArray: If a ByteArray object's `shareable` property is `true`, the same underlying memory is used for instances of that ByteArray in all workers. Because code in multiple workers can access the shared memory at the same time, your code should use the mechanisms described in the `ByteArray.shareable` property description to avoid problems from unexpected data changes.

Several runtime APIs are not available in code running in a background worker. These primarily consist of APIs related to user input and output mechanisms, or operating system elements like windows and dragging. As a rule, for any API that isn't supported in all contexts, use the `isSupported`, `available`, and similar properties to check whether the API is available in the background worker context before attempting to use the API.

Note: Native Extensions are not supported for background and secondary workers.

Workers are useful because they decrease the chances of the frame rate dropping due to the main rendering thread being blocked by other code. However, workers require additional system memory and CPU use, which can be costly to overall application performance. Because each worker uses its own instance of the runtime virtual machine, even the overhead of a trivial worker can be large. When using workers, test your code across all your target platforms to ensure that the demands on the system are not too large. Adobe recommends that you do not use more than one or two background workers in a typical scenario.

View the examples

More examples

Understanding workers and concurrency
Creating and managing workers

Learn more

Using ActionScript Workers by Lee Brimelow
Intro to AS3 Workers: Image Processing by Shawn Blais
Multithreaded Physics: Using AS3 Workers for a physics engine by Shawn Blais

Related API Elements

WorkerDomain class
WorkerDomain.createWorker() method
MessageChannel class

Public Properties
Hide Inherited Public Properties
Show Inherited Public Properties

| Property | Defined By
--- | --- | ---
|  | constructor : Object A reference to the class object or constructor function for a given object instance. | Object
|  | current : Worker [static] [read-only] Provides access to the worker that contains the current code | Worker
|  | isPrimordial : Boolean [read-only] Indicates whether this worker is the primordial worker. | Worker
|  | isSupported : Boolean [static] [read-only] Indicates whether the current runtime context supports the use of Worker objects for concurrent code execution. | Worker
|  | state : String [read-only] The current state of the worker in its lifecycle. | Worker

Public Methods
Hide Inherited Public Methods
Show Inherited Public Methods

| Method | Defined By
--- | --- | ---
|  | addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void [override] Registers an event listener object with an EventDispatcher object so that the listener receives notification of an event. | Worker
|  | createMessageChannel(receiver:Worker):MessageChannel Creates a new MessageChannel instance to send messages from the worker on which the method is called to another receiver worker. | Worker
|  | dispatchEvent(event:Event):Boolean Dispatches an event into the event flow. | EventDispatcher
|  | getSharedProperty(key:String):* Retrieves a value stored in this worker with a named key. | Worker
|  | hasEventListener(type:String):Boolean Checks whether the EventDispatcher object has any listeners registered for a specific type of event. | EventDispatcher
|  | hasOwnProperty(name:String):Boolean Indicates whether an object has a specified property defined. | Object
|  | isPrototypeOf(theClass:Object):Boolean Indicates whether an instance of the Object class is in the prototype chain of the object specified as the parameter. | Object
|  | propertyIsEnumerable(name:String):Boolean Indicates whether the specified property exists and is enumerable. | Object
|  | removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void [override] Removes a listener from the EventDispatcher object. | Worker
|  | setPropertyIsEnumerable(name:String, isEnum:Boolean = true):void Sets the availability of a dynamic property for loop operations. | Object
|  | setSharedProperty(key:String, value:*):void Provides a named value that is available to code running in the worker's swf. | Worker
|  | start():void Starts the execution of the worker. | Worker
|  | terminate():Boolean Stops this worker's code execution. | Worker
|  | toLocaleString():String Returns the string representation of this object, formatted according to locale-specific conventions. | Object
|  | toString():String Returns the string representation of the specified object. | Object
|  | valueOf():Object Returns the primitive value of the specified object. | Object
|  | willTrigger(type:String):Boolean Checks whether an event listener is registered with this EventDispatcher object or any of its ancestors for the specified event type. | EventDispatcher

Events
Click for more information on events
Hide Inherited Events
Show Inherited Events

| Event | Summary | Defined By
--- | --- | --- | ---
|  | activate | [broadcast event] Dispatched when the Flash Player or AIR application gains operating system focus and becomes active. | EventDispatcher
|  | deactivate | [broadcast event] Dispatched when the Flash Player or AIR application operating loses system focus and is becoming inactive. | EventDispatcher
|  | workerState | Dispatched when the value of the worker's state property changes. | Worker

Property Detail

### current

property

`current:Worker` [read-only]

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Provides access to the worker that contains the current code

Implementation
`public static function get current():Worker`

### isPrimordial

property

`isPrimordial:Boolean` [read-only]

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Indicates whether this worker is the primordial worker.

The primordial worker is the worker in which the initial swf is running. This worker controls the rendering to the screen.

This property can be used to architect an application where the primordial worker and the background worker are two instances of the same swf file. The alternative is to structure your code so that the background worker uses different code compiled to a different swf from the primorial worker.

Implementation
`public function get isPrimordial():Boolean`

### isSupported

property

`isSupported:Boolean` [read-only]

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Indicates whether the current runtime context supports the use of Worker objects for concurrent code execution.

If concurrency is available, this property's value is `true`.

Implementation
`public static function get isSupported():Boolean`

### state

property

`state:String` [read-only]

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

The current state of the worker in its lifecycle. The possible values for this property are defined in the WorkerState class.

Implementation
`public function get state():String`

Related API Elements

flash.system.WorkerState
Method Detail

### addEventListener

()
method

`override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Registers an event listener object with an EventDispatcher object so that the listener receives notification of an event. You can register event listeners on all nodes in the display list for a specific type of event, phase, and priority.

After you successfully register an event listener, you cannot change its priority through additional calls to `addEventListener()`. To change a listener's priority, you must first call `removeListener()`. Then you can register the listener again with the new priority level.

Keep in mind that after the listener is registered, subsequent calls to `addEventListener()` with a different `type` or `useCapture` value result in the creation of a separate listener registration. For example, if you first register a listener with `useCapture` set to `true`, it listens only during the capture phase. If you call `addEventListener()` again using the same listener object, but with `useCapture` set to `false`, you have two separate listeners: one that listens during the capture phase and another that listens during the target and bubbling phases.

You cannot register an event listener for only the target phase or the bubbling phase. Those phases are coupled during registration because bubbling applies only to the ancestors of the target node.

If you no longer need an event listener, remove it by calling `removeEventListener()`, or memory problems could result. Event listeners are not automatically removed from memory because the garbage collector does not remove the listener as long as the dispatching object exists (unless the `useWeakReference` parameter is set to `true`).

Copying an EventDispatcher instance does not copy the event listeners attached to it. (If your newly created node needs an event listener, you must attach the listener after creating the node.) However, if you move an EventDispatcher instance, the event listeners attached to it move along with it.

If the event listener is being registered on a node while an event is being processed on this node, the event listener is not triggered during the current phase but can be triggered during a later phase in the event flow, such as the bubbling phase.

If an event listener is removed from a node while an event is being processed on the node, it is still triggered by the current actions. After it is removed, the event listener is never invoked again (unless registered again for future processing).

Parameters

| `type:String` — The type of event.
| `listener:Function` — The listener function that processes the event. This function must accept an Event object as its only parameter and must return nothing, as this example shows: Copy
    function(evt:Event):void
The function can have any name.
| `useCapture:Boolean` (default = `false`)`` — Determines whether the listener works in the capture phase or the target and bubbling phases. If `useCapture` is set to `true`, the listener processes the event only during the capture phase and not in the target or bubbling phase. If `useCapture` is `false`, the listener processes the event only during the target or bubbling phase. To listen for the event in all three phases, call `addEventListener` twice, once with `useCapture` set to `true`, then again with `useCapture` set to `false`.
| `priority:int` (default = `0`)`` — The priority level of the event listener. The priority is designated by a signed 32-bit integer. The higher the number, the higher the priority. All listeners with priority n are processed before listeners of priority n-1. If two or more listeners share the same priority, they are processed in the order in which they were added. The default priority is 0.
| `useWeakReference:Boolean` (default = `false`)`` — Determines whether the reference to the listener is strong or weak. A strong reference (the default) prevents your listener from being garbage-collected. A weak reference does not. Class-level member functions are not subject to garbage collection, so you can set `useWeakReference` to `true` for class-level member functions without subjecting them to garbage collection. If you set `useWeakReference` to `true` for a listener that is a nested inner function, the function will be garbage-collected and no longer persistent. If you create references to the inner function (save it in another variable) then it is not garbage-collected and stays persistent.

### createMessageChannel

()
method

`public function createMessageChannel(receiver:Worker):MessageChannel`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Creates a new MessageChannel instance to send messages from the worker on which the method is called to another receiver worker. Code in the worker that creates the MessageChannel object can use it to send one-way messages to the Worker object specified as the `receiver` argument.

Although a MessageChannel instance can be used to send messages and data from one Worker instance to another, at least one MessageChannel instance needs to be passed to a child Worker as a shared property by calling the Worker object's `setSharedProperty()` method.

Copy

        outgoingChannel = Worker.current.createMessageChannel(bgWorker);
        incomingChannel = bgWorker.createMessageChannel(Worker.current);

        bgWorker.setSharedProperty("incoming", outgoingChannel);
        bgWorker.setSharedProperty("outgoing", incomingChannel);

        // listen for messages from the receiving MessageChannel
        // This event is triggered when the background sends a message to this worker
        incomingChannel.addEventListener(Event.CHANNEL_MESSAGE, incomingMessageHandler);

Parameters

| `receiver:Worker` — The worker that will receive messages transmitted via the created message channel

Returns

| `MessageChannel` — The MessageChannel object created by the operation

### getSharedProperty

()
method

`public function getSharedProperty(key:String):*`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Retrieves a value stored in this worker with a named key.

Code in a child worker can call this method to retrieve a value as early as in the constructor of the worker swf's main class.

Parameters

| `key:String` — The name of the shared property to retrieve

Returns

| `*` — The shared property value stored with the specified key, or `null` if no value is stored for the specified key

### removeEventListener

()
method

`override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Removes a listener from the EventDispatcher object. If there is no matching listener registered with the EventDispatcher object, a call to this method has no effect.

Parameters

| `type:String` — The type of event.
| `listener:Function` — The listener object to remove.
| `useCapture:Boolean` (default = `false`)`` — Specifies whether the listener was registered for the capture phase or the target and bubbling phases. If the listener was registered for both the capture phase and the target and bubbling phases, two calls to `removeEventListener()` are required to remove both, one call with `useCapture()` set to `true`, and another call with `useCapture()` set to `false`.

### setSharedProperty

()
method

`public function setSharedProperty(key:String, value:*):void`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Provides a named value that is available to code running in the worker's swf.

You can call this method before calling the worker's `start()` method. In that case the shared property is available to code in the worker's swf at construction time.

The value passed to the `value` parameter can be almost any object. Other than the exceptions noted below, any object that is passed to the `value` parameter is not passed by reference. Any changes made to the object in one worker after `setSharedProperty()` is called are not carried over to the other worker. The object is copied by serializing it to AMF3 format and deserializing it into a new object in the receiving worker. For this reason, any object that can't be serialized in AMF3 format, including display objects, can't be passed to the `value` parameter. In order for a custom class to be passed properly, the class definition must be registered using the `flash.net.registerClassAlias()` function or `[RemoteClass]` metadata. With either technique the same alias must be used for both worker's versions of the class.

There are five types of objects that are an exception to the rule that objects aren't shared between workers:

- Worker
- MessageChannel
- shareable ByteArray (a ByteArray object with its `shareable` property set to `true`
- Mutex
- Condition

If you pass an instance of these objects to the `value` parameter, each worker has a reference to the same underlying object. Changes made to an instance in one worker are immediately available in other workers. In addition, if you pass the same instance of these objects more than once using `setSharedProperty()`, the runtime doesn't create a new copy of the object in the receiving worker. Instead, the same reference is re-used, reducing system memory use.

Calling this method with `null` or `undefined` for the `value` argument clears any previously-set value for the specified `key` argument. Cleaning up a value in this way removes the reference to it, allowing it to be garbage collected.

You can use any String value in the key argument. These shared properties are available to any code that has access to a worker. To avoid unintentionally overwriting a value, consider using a prefix, suffix, or similar mechanism to attempt to make your key names unique.

Parameters

| `key:String` — The name under which the shared property is stored.
| `value:*` — The value of the shared property.

Related API Elements

MessageChannel class
ByteArray.shareable property
Mutex class
Condition class

### start

()
method

`public function start():void`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Starts the execution of the worker. The runtime creates the worker thread and calls the constructor of the worker swf's main class.

This operation is asynchronous. Once the worker startup is complete, it changes its `state` property to `WorkerState.RUNNING` and dispatches a `workerState` event.

### terminate

()
method

`public function terminate():Boolean`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Stops this worker's code execution. Calling this method aborts any current ActionScript in the worker's swf.

Returns

| `Boolean` — `true` if code in the worker was running and interrupted, or `false` if the worker was never started

Event Detail

### workerState

Event

Event Object Type: `flash.events.Event`
property Event.type = `flash.events.Event.WORKER_STATE`

Language Version: | ActionScript 3.0

Runtime Versions: | Flash Player 11.4, AIR 3.4

Dispatched when the value of the worker's `state` property changes.

The `Event.WORKER_STATE` constant defines the value of the `type` property of a `workerState` event object.

This event has the following properties:

Property | Value
--- | ---
`bubbles` | `false`
`cancelable` | `false`; there is no default behavior to cancel.
`currentTarget` | The object that is actively processing the Event object with an event listener.
`target` | The object that dispatched this event.

Examples How to use this example
WorkerExample.as
The following example demonstrates using a Worker object to perform a task in the background. It also shows communication between the parent worker and the background worker, including passing a custom class object between the workers.

This example consists of three ActionScript classes: WorkerExample is the main class and the parent worker. BackgroundWorker is the class that does the background work. It's compiled as the main class of the background worker swf. CountResult is a custom class that's used to pass data between the two workers as a single object rather than as multiple values.

In this example, the background worker counts in a loop up to a number specified by the parent worker. As it makes progress in its work it sends progress messages to the parent worker. Finally, when the count is finished the background worker sends a message to the parent worker notifying it that it finished and how long it took to count.

The WorkerExample class is the main class of the swf, so it is the main class of the primordial worker. In the `initialize()` method, the code creates the background worker object using the bytes of the BackgroundWorker class, which are embedded using an `[Embed]` tag.

After creating the background worker by calling `WorkerDomain.createWorker()`, the code sets up communication between the workers. First the code creates a set of MessageChannel objects. It passes them to the background worker by calling its `setSharedProperty()` method. Finally it registers for the background Worker object's `workerState` event and starts up the worker by calling its `start()` method.

As the background worker does its work, it sends progress (and eventually result) messages to the parent worker. The parent worker uses this information to update the progress bar and the text indicator.

Copy

    package
    {
        import com.adobe.example.vo.CountResult;

        import flash.display.Shape;
        import flash.display.Sprite;
        import flash.display.StageAlign;
        import flash.display.StageScaleMode;
        import flash.events.Event;
        import flash.net.registerClassAlias;
        import flash.system.MessageChannel;
        import flash.system.Worker;
        import flash.system.WorkerDomain;
        import flash.system.WorkerState;
        import flash.text.TextField;
        import flash.text.TextFormat;
        import flash.text.TextFormatAlign;
        import flash.utils.ByteArray;

        public class WorkerExample extends Sprite
        {
            // ------- Embed the background worker swf as a ByteArray -------
            [Embed(source="../workerswfs/BackgroundWorker.swf", mimeType="application/octet-stream")]
            private static var BackgroundWorker_ByteClass:Class;
            public static function get BackgroundWorker():ByteArray
            {
                return new BackgroundWorker_ByteClass();
            }

            private var bgWorker:Worker;
            private var bgWorkerCommandChannel:MessageChannel;
            private var progressChannel:MessageChannel;
            private var resultChannel:MessageChannel;

            public function WorkerExample()
            {
                initialize();
            }

            private function initialize():void
            {
                // create the user interface
                setupStage();
                createStatusText();
                createProgressBar();

                // Register the alias so we can pass CountResult objects between workers
                registerClassAlias("com.adobe.test.vo.CountResult", CountResult);

                // Create the background worker
                bgWorker = WorkerDomain.current.createWorker(BackgroundWorker);

                // Set up the MessageChannels for communication between workers
                bgWorkerCommandChannel = Worker.current.createMessageChannel(bgWorker);
                bgWorker.setSharedProperty("incomingCommandChannel", bgWorkerCommandChannel);

                progressChannel = bgWorker.createMessageChannel(Worker.current);
                progressChannel.addEventListener(Event.CHANNEL_MESSAGE, handleProgressMessage)
                bgWorker.setSharedProperty("progressChannel", progressChannel);

                resultChannel = bgWorker.createMessageChannel(Worker.current);
                resultChannel.addEventListener(Event.CHANNEL_MESSAGE, handleResultMessage);
                bgWorker.setSharedProperty("resultChannel", resultChannel);

                // Start the worker
                bgWorker.addEventListener(Event.WORKER_STATE, handleBGWorkerStateChange);
                bgWorker.start();
            }

            private function handleBGWorkerStateChange(event:Event):void
            {
                if (bgWorker.state == WorkerState.RUNNING)
                {
                    _statusText.text = "Background worker started";
                    bgWorkerCommandChannel.send(["startCount", 100000000]);
                }
            }

            private function handleProgressMessage(event:Event):void
            {
                var percentComplete:Number = progressChannel.receive();
                setPercentComplete(percentComplete);
                _statusText.text = Math.round(percentComplete).toString() + "% complete";
            }

            private function handleResultMessage(event:Event):void
            {
                var result:CountResult = resultChannel.receive() as CountResult;
                setPercentComplete(100);
                _statusText.text = "Counted to " + result.countTarget + " in " + (Math.round(result.countDurationSeconds * 10) / 10) + " seconds";
            }

            // ------- Create UI -------

            private var _currentPercentComplete:int = 0;
            private var _needsValidation:Boolean = false;
            private var _statusText:TextField;
            private var _progressBarRect:Shape;
            private var _progressBar:Shape;

            private function setupStage():void
            {
                stage.align = StageAlign.TOP_LEFT;
                stage.scaleMode = StageScaleMode.NO_SCALE;
                stage.stageWidth = 800;
                stage.stageHeight = 600;
                stage.color = 0xffffff;
            }

            private function createStatusText():void
            {
                _statusText = new TextField();
                _statusText.width = 400;
                _statusText.height = 25;
                _statusText.x = (stage.stageWidth - _statusText.width) / 2;
                _statusText.y = 150;

                var statusTextFormat:TextFormat = new TextFormat();
                statusTextFormat.color = 0xeeeeee;
                statusTextFormat.font = "Verdana";
                statusTextFormat.align = TextFormatAlign.CENTER;
                statusTextFormat.size = 16;
                _statusText.defaultTextFormat = statusTextFormat;
                _statusText.wordWrap = false;
                _statusText.opaqueBackground = 0x999999;
                _statusText.selectable = false;

                _statusText.text = "Initializing...";

                addChild(_statusText);
            }

            private function createProgressBar():void
            {
                _progressBarRect = new Shape();
                _progressBarRect.graphics.beginFill(0x000000, 0);
                _progressBarRect.graphics.lineStyle(2, 0x000000);
                _progressBarRect.graphics.drawRect(0, 0, 400, 30);
                _progressBarRect.graphics.endFill();

                _progressBarRect.x = (stage.stageWidth - _progressBarRect.width) / 2;
                _progressBarRect.y = 100;

                addChild(_progressBarRect);

                _progressBar = new Shape();
                _progressBar.graphics.beginFill(0x0000ee);
                _progressBar.graphics.drawRect(0, 0, 391, 21);
                _progressBar.x = _progressBarRect.x + 4;
                _progressBar.y = _progressBarRect.y + 4;

                addChild(_progressBar);

                _progressBar.scaleX = 0;
            }

            private function setPercentComplete(percentComplete:int):void
            {
                if (_currentPercentComplete == percentComplete)
                    return;

                _currentPercentComplete = percentComplete;
                invalidateValue();
            }

            private function invalidateValue():void
            {
                if (_needsValidation)
                    return;

                _needsValidation = true;
                addEventListener(Event.EXIT_FRAME, validate);
            }

            private function validate(event:Event):void
            {
                removeEventListener(Event.EXIT_FRAME, validate);
                _needsValidation = false;

                _redrawProgressBar();
            }

            private function _redrawProgressBar():void
            {
                _progressBar.scaleX = _currentPercentComplete / 100;
            }
        }
    }

WorkerExample.BackgroundWorker.as
This class contains the code for the background worker. This class is compiled into its own swf file. That swf file is then embedded in the main swf.

In the `initialize()` method, it receives the MessageChannel objects that the parent worker passed in. They are used for communication between the two workers.

The parent worker calls the `send()` method on the `commandChannel` message channel to send a message. Inside the background worker, the runtime then dispatches the `channelMessage` event by calling the `handleCommandMessage()` method.

The background worker's actual work takes place in the `count()` method. As the background worker proceeds with its count, it sends progress messages to the parent worker by calling the `send()` method on the `progressChannel` MessageChannel object. When it finishes counting, it calls the `send()` method on the `resultChannel` MessageChannel object.

Copy

    package com.adobe.example.workers
    {
        import com.adobe.example.vo.CountResult;

        import flash.display.Sprite;
        import flash.events.Event;
        import flash.net.registerClassAlias;
        import flash.system.MessageChannel;
        import flash.system.Worker;
        import flash.utils.getTimer;

        public class BackgroundWorker extends Sprite
        {
            private var commandChannel:MessageChannel;
            private var progressChannel:MessageChannel;
            private var resultChannel:MessageChannel;

            public function BackgroundWorker()
            {
                initialize();
            }

            private function initialize():void
            {
                registerClassAlias("com.adobe.test.vo.CountResult", CountResult);

                // Get the MessageChannel objects to use for communicating between workers
                // This one is for receiving messages from the parent worker
                commandChannel = Worker.current.getSharedProperty("incomingCommandChannel") as MessageChannel;
                commandChannel.addEventListener(Event.CHANNEL_MESSAGE, handleCommandMessage);
                // These are for sending messages to the parent worker
                progressChannel = Worker.current.getSharedProperty("progressChannel") as MessageChannel;
                resultChannel = Worker.current.getSharedProperty("resultChannel") as MessageChannel;
            }

            private function handleCommandMessage(event:Event):void
            {
                if (!commandChannel.messageAvailable)
                    return;

                var message:Array = commandChannel.receive() as Array;

                if (message != null && message[0] == "startCount")
                {
                    count(uint(message[1]));
                }
            }

            private function count(targetValue:uint):void
            {
                var startTime:int = getTimer();
                var onePercent:uint = uint(Math.ceil(targetValue / 100));
                var oneHalfPercent:Number = onePercent / 2;

                var i:uint = 0;
                while (i < targetValue)
                {
                    i++;
                    // only send progress messages every one-half-percent milestone
                    // to avoid flooding the message channel
                    if (i % oneHalfPercent == 0)
                    {
                        progressChannel.send(i / onePercent);
                    }
                }

                var elapsedTime:int = getTimer() - startTime;
                var result:CountResult = new CountResult(targetValue, elapsedTime / 1000);
                resultChannel.send(result);

                trace("counted to", targetValue.toString(), "in", elapsedTime, "milliseconds");
            }
        }
    }

WorkerExample.CountResult.as
This class defines the CountResult value object. This is a custom class that is passed between the two workers. In order to pass a custom class between the workers, each worker calls the `registerClassAlias()` method, using the same alias name.
Copy

    package com.adobe.example.vo
    {
        public class CountResult
        {
            public function CountResult(countTarget:uint=0, countTime:Number=0)
            {
                this.countTarget = countTarget;
                this.countDurationSeconds = countTime;
            }

            public var countTarget:uint;
            public var countDurationSeconds:Number;
        }
    }

IOSWorkerExample.as
Concurrency support for iOS has been added from AIR Version 19 and onwards. Everything remains coherent to Flash/AIR Android, except for differences in loading of SWF and passing byte array to worker.

1. As dynamically loading a remote SWF that contains ActionScript code doesn't work, the remote SWF has to be passed to worker as a stripped SWF. 2. Embedding SWFs(having ABC code) using [Embed] tag will not work on iOS. Each additional worker is created from a separate SWF. To create a new instance of the Worker class, pass a ByteArray with the bytes of the background worker's SWF as an argument to the WorkerDomain class's `createWorker()` method.

There are two common ways to access the bytes of a SWF for this purpose on iOS: The first way is using `Loader` to load an external SWF file and the second is using `URLLoader` to load SWF file.

The following example uses `Loader` API to load the SWF file.

Copy

    package
    {
        import flash.display.Loader;
        import flash.display.Sprite;
        import flash.display.StageAlign;
        import flash.display.StageScaleMode;
        import flash.events.Event;
        import flash.events.IOErrorEvent;
        import flash.net.URLRequest;
        import flash.system.ApplicationDomain;
        import flash.system.LoaderContext;
        import flash.system.MessageChannel;
        import flash.system.Worker;
        import flash.system.WorkerDomain;
        import flash.text.TextField;
        import flash.text.TextFormat;

        public class IOSWorkerExample extends Sprite
        {
            public var worker:Worker;
            public var bm:MessageChannel;
            public var mb:MessageChannel;
            public var tf:TextField;
            public var tfrmt:TextFormat;

            public function IOSWorkerExample()
            {
                super();

                stage.align = StageAlign.TOP_LEFT;
                stage.scaleMode = StageScaleMode.NO_SCALE;

                tf=new TextField();
                tfrmt= new TextFormat()
                tfrmt.size=80;
                tf.textColor = 0xFFFFF;
                tf.defaultTextFormat=tfrmt;

                addChild(tf);
                //creating the urlRequest object that references the background worker.
                var _urlRequest:URLRequest = new URLRequest("IOSBackWorker.swf");
                var _loader:Loader = new Loader();
                var _lc:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain, null);

                _loader.contentLoaderInfo.addEventListener(Event.COMPLETE,completeHandler);
                _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
                _loader.load(_urlRequest, _lc);

            }
            // This function is called once the swf loading is completed
            public function completeHandler(e:Event):void
            {

                worker = WorkerDomain.current.createWorker(e.target.bytes);
                bm = worker.createMessageChannel(Worker.current);
                mb = Worker.current.createMessageChannel(worker);

                worker.setSharedProperty("btm", bm);
                worker.setSharedProperty("mtb", mb);
                //adding event handler on message receive from background
                bm.addEventListener(Event.CHANNEL_MESSAGE, onBackToMain);

                worker.start();
                bm.receive(true);

            }

            public function errorHandler(e:IOErrorEvent):void
            {

                trace("In IO ErrorEvent Handler  "+e.text);
            }

            //This function is called when the main thread receives the message from the background worker.
            public function onBackToMain(event:Event):void
            {

                if(bm.messageAvailable)
                {
                    // displaying the percentages based on the message received from the background.
                    var progress:Number = bm.receive();
                    trace("progress   "+progress);

                    tf.text= progress.toString();
                }
            }

        }
    }

IOSBackWorker.as
As the code is being executed while load function of `Loader` is called, we need to put `isPrimordial` property check in background worker as shown in this example.
Copy

    package
    {
        import flash.display.Sprite;
        import flash.system.MessageChannel;
        import flash.system.Worker;
        import flash.utils.ByteArray;
        import flash.utils.getTimer;

        public class IOSBackWorker extends Sprite
        {
            private var memory:ByteArray = new ByteArray();
            private var bm:MessageChannel;
            private var mb:MessageChannel;

            public function IOSBackWorker()
            {
                if(!Worker.current.isPrimordial)
                {
                    memory.shareable = true;
                    // creating objects of message channels
                    bm = Worker.current.getSharedProperty("btm");
                    mb = Worker.current.getSharedProperty("mtb");

                    // calculating the percentage
                    trace("message came");
                    var j:Number =1;
                    while (j<1000)
                    {
                        for(var i=0;i<100;i++){}
                        var startTime=getTimer();
                        // adding delay
                        while (getTimer()-startTime <100);

                        trace(j, (100*j)/1000);
                        var progress:Number=(100*j)/1000;
                        // sending the percentage to the main thread
                        bm.send(progress);

                        j++;
                    }
                }
            }
        }
    }

© 2015 Adobe Systems Incorporated. All rights reserved.
Mon Apr 10 2017, 12:28 PM -07:00