https://github.com/evgeniy-polyakov/as3-async
Asynchronous tasks in AS3
https://github.com/evgeniy-polyakov/as3-async
actionscript async flash-api-wrappers
Last synced: 4 months ago
JSON representation
Asynchronous tasks in AS3
- Host: GitHub
- URL: https://github.com/evgeniy-polyakov/as3-async
- Owner: evgeniy-polyakov
- License: mit
- Created: 2016-05-01T11:13:01.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2019-02-15T17:12:03.000Z (over 7 years ago)
- Last Synced: 2025-08-01T03:51:42.347Z (10 months ago)
- Topics: actionscript, async, flash-api-wrappers
- Language: ActionScript
- Homepage:
- Size: 377 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# as3-async
Asynchronous operations in AS3.
* [About](#about)
* [Getting started](#getting-started)
+ [Define asynchronous sequence](#define-asynchronous-sequence)
+ [Run asynchronous sequence](#run-asynchronous-sequence)
+ [Passing arguments](#passing-arguments)
+ [Handling errors](#handling-errors)
+ [Branching](#branching)
+ [Canceling execution](#canceling-execution)
+ [Adding new tasks](#adding-new-tasks)
* [Flash API wrappers](#flash-api-wrappers)
+ [Loader tasks](#loader-tasks)
+ [Timeout tasks](#timeout-tasks)
+ [File reference tasks](#file-reference-tasks)
* [Task definition](#task-definition)
+ [Nested `async`](#nested-async)
* [Asynchronous concurrence](#asynchronous-concurrence)
+ [Continue when all complete](#continue-when-all-complete)
+ [Continue when any complete](#continue-when-any-complete)
##About
Asynchronous AS3 tasks are intended to simplify event flow usually in loading or animation sequences.
1. Asynchronous tasks are very close to [JS Promises](https://www.promisejs.org/) however they are written in more object oriented style.
2. There are wrappers for commonly used Flash API.
3. No events are created and dispatched, we use direct callbacks because they are more fast.
4. Error handling is provided.
5. Synchronous functions can be used as asynchronous tasks.
## Getting started
### Define asynchronous sequence
Asynchronous sequence is defined using fluent interface where each method accepts `ITask` or `Function`. For example the following code defines a sequence that loads "some.swf", then "some.xml" and calls the callback function when all tasks are complete:
```actionscript
async(new LoaderTask("some.swf"))
.then(new URLLoaderTask("some.xml"))
.then(function():void {trace("complete");});
```
### Run asynchronous sequence
Once the sequence is defined it should be started or stored in a variable:
```actionscript
var s:IAsync = async(new LoaderTask("some.swf"))
.then(new URLLoaderTask("some.xml"))
.then(function():void {trace("complete");});
s.await();
```
> Note that the sequence can run only once. It can not be started again if complete or canceled.
### Passing arguments
Each task in the sequence has input and output parameters (like single argument and return value in a function). The output of previous task is input of the next task and so on. The first argument in the sequence can be passed to `await` method. The following example uses functions to display arguments, however you can define an asynchronous task that will be using arguments the same way:
```actionscript
async(function(arg:String):void {trace(arg);}) // start-sequence
.then(new LoaderTask("some.swf"))
.then(function(loader:Loader):void {trace(loader.content);}); // [object MovieClip]
.then(new URLLoaderTask("some.xml"))
.then(function(loader:URLLoader):void {trace(loader.data);}); //
.await("start-sequence");
```
Actually you can pass your arguments directly to `async` and `then` method if the arguments are known when the sequence is defined:
```actionscript
async("start-sequence")
.then(function(arg:String):void {trace(arg);}) // start-sequence
.then(new LoaderTask("some.swf"))
.then("continue-sequence")
.then(function(arg:String):void {trace(arg);}) // continue-sequence
.then(new URLLoaderTask("some.xml"))
.await();
```
Notice that `Error` and `ErrorEvent` are considered as errors and they are "thrown" (see **Handling errors** section). For example here `URLLoaderTask` is never executed:
```actionscript
async(new LoaderTask("some.swf"))
.then(new Error())
.then(new URLLoaderTask("some.xml"))
.await();
```
### Handling errors
The task can complete successfully or not (like fulfill and reject in promises). If it is not successful it "throws" an error and no further tasks are executed until an error handler is found. The error handler is a task passed to method `except`. For example single error handler for all tasks (note that `URLLoaderTask` is not executed if `URLLoaderTask` fails):
```actionscript
async(new LoaderTask("some.swf"))
.then(new URLLoaderTask("some.xml"))
.except(function(error:*):void {trace(error);}); // IOErrorEvent or SecurityErrorEvent
.then(function():void {trace("complete");}); // complete
.await();
```
Also you can define many error handlers for each of tasks (in that case `URLLoaderTask` is executed even if `URLLoaderTask` fails):
```actionscript
async(new LoaderTask("some.swf"))
.except(function(error:*):void {trace(error);}); // IOErrorEvent or SecurityErrorEvent
.then(new URLLoaderTask("some.xml"))
.except(function(error:*):void {trace(error);}); // IOErrorEvent or SecurityErrorEvent
.then(function():void {trace("complete");}); // complete
.await();
```
Errors thrown by functions are also handled the same way:
```actionscript
async(function():void {throw new Error();})
.except(function(error:*):void {trace(error);}); // Error
.then(function():void {trace("complete");}); // complete
.await();
```
Error handler can be any task, so for example you can execute `URLLoaderTask` only if `LoaderTask` fails:
```actionscript
async(new LoaderTask("some.swf"))
.except(new URLLoaderTask("some.xml"));
.then(function():void {trace("complete");});
.await();
```
### Branching
When you need a task to be executed only if the previous tasks are successful you can use branching. The following example loads one or another xml depending on success of `URLLoaderTask`:
```actionscript
async(new LoaderTask("some.swf"))
.then(new URLLoaderTask("success.xml"),
new URLLoaderTask("failure.xml"))
.then(function():void {trace("complete");}); // complete
.await();
```
Branching and error handling can be combined in any way. For example here we can handle errors of whatever `URLLoaderTask` is executed:
```actionscript
async(new LoaderTask("some.swf"))
.then(new URLLoaderTask("success.xml"),
new URLLoaderTask("failure.xml"))
.except(function(error:*):void {trace(error);}); // IOErrorEvent or SecurityErrorEvent
.then(function():void {trace("complete");}); // complete
.await();
```
### Canceling execution
Execution of asynchronous sequence can be interrupted at any moment. But you need to keep a reference to the asynchronous sequence to do that. For example canceling loading in 100 ms regardless of which file exactly is being loaded at the moment:
```actionscript
var s:IAsync = async(new LoaderTask("some.swf"))
.then(new URLLoaderTask("some.xml"));
s.await();
setTimeout(function():void {s.cancel();}, 100);
```
### Adding new tasks
When your sequence is already running you can still add new tasks to it. Those new tasks will be executed as usual:
```actionscript
var s:IAsync = async(new LoaderTask("some.swf"))
s.await();
s.then(new URLLoaderTask("some.xml"));
```
> Be careful with this feature. First of all don't add error handlers to the running sequence because you can get error before adding the handler. Also call `await` after adding new tasks to make sure the sequence continues if it is already complete.
This feature can be usefull when implementing asynchronous queue that executes tasks one after another:
```actionscript
public class Queue {
private var _async:IAsync = async();
public function add(task:Object, onComplete:Function, onError:Function):void {
_async
.then(async(task).then(onComplete, onError))
.await();
}
}
```
## Flash API wrappers
### Loader tasks
- `LoaderTask(source:*, context:LoaderContext = null)` uses [Loader](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Loader.html) object to load a flash movie from the given url, `URLRequest`, `ByteArray` object or class; returns `Loader` object; throws `IOErrorEvent`, `SecurityErrorEvent`.
- `URLLoaderTask(source:Object, format:String = "text")` uses [URLLoader](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLLoader.html) to load a file from the given url or `URLRequest`; returns `URLLoader` object; throws `IOErrorEvent`, `SecurityErrorEvent`.
### Timeout tasks
- `TimeoutTask(milliseconds=0)` waits for the given number of milliseconds.
- `FramesTask(frames=0)` waits for the given number of frames.
### File reference tasks
The following tasks use [FileReference](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/FileReference.html) to browse, download or upload files:
- `BrowseFileTask(filters:Array = null)` returns `FileReference` with the selected file or throws `Event.CANCEL` if no file is selected.
- `DownloadFileTask(source:Object, defaultFileName:String = null)` returns `FileReference` with the downloaded file or throws `Event.CANCEL`, `IOErrorEvent`, `SecurityErrorEvent`.
- `UploadFileTask(source:Object, dataFieldName:String = "Filedata")` gets `FileReference` as argument and uploads it to the given url, must be called after `BrowseFileTask`.
- `LoadFileTask()` gets `FileReference` as argument and loads file content, must be called after `BrowseFileTask`.
## Task definition
### Nested `async`
`async` function itself returns a task so you can simply pass one `async` into another. This is useful in case of complex error handling and branching. For example we can load two files sequentially if the main task fails:
```actionscript
async(new LoaderTask("some.swf"))
.except(async(new URLLoaderTask("file1.xml"))
.then(new URLLoaderTask("file2.xml")))
.then(function():void {trace("complete");});
.await();
```
### Factory function
Factory function allows to define a task based on result of the previous task. The function should return `ITask`. For example load xml or json files based on config value:
```actionscript
async(new URLLoaderTask("config.txt"))
.then(function(loader:URLLoader):ITask {
if (loader.data == "load-xml") {
return async(new URLLoaderTask("file1.xml"))
.then(new URLLoaderTask("file2.xml"));
} else {
return new URLLoaderTask("file.json");
}
})
.then(function():void {trace("complete");});
.await();
```
### Promise style
Using factory function you can keep an instance of `Task` in closure and return or throw depending of the result of your asynchronous method. The following example waits for 100 ms and returns or throws based on config value:
```actionscript
async(new URLLoaderTask("config.txt"))
.then(function(loader:URLLoader):ITask {
var task:Task = new Task();
if (loader.data == "ok") {
setTimeout(function():void {task.onReturn("Success");}, 100);
} else {
setTimeout(function():void {task.onThrow(new Error());}, 100);
}
return task;
})
.except(function(error:*):void {trace(error);}) // Error
.then(function(value:*):void {trace(value);}); // Success
.await();
```
### Extending `Task`
If you want to implement completely custom task it could be done by extending `Task` class. Then you will be able to use that task in a sequence, await and cancel it like any other task. The following example defines a task that waits for 100 ms and returns or throws based on the given arguments:
```actionscript
public class MyTask extends Task {
private var _timeoutId;
override protected function onAwait():void {
if (args == "ok") {
_timeoutId = setTimeout(function():void {onReturn("Success");}, 100);
} else {
_timeoutId = setTimeout(function():void {onThrow(new Error());}, 100);
}
}
override protected function onCancel():void {
clearTimeout(_timeoutId);
}
}
```
### Implementing `ITask`
If you want to implement a custom task that extends your base class you can implement `ITask` interface and use inner `Task` object. The following example defines a task that waits for 100 ms and returns or throws based on the given arguments:
```actionscript
public class MyTask extends MyBaseClass implements ITask {
private var _innerTask:Task = new Task();
private var _timeoutId;
public function await(args:Object = null, result:IResult = null):void {
_innerTask.await(args, result);
if (args == "ok") {
_timeoutId = setTimeout(function():void {_innerTask.onReturn("Success", this);}, 100);
} else {
_timeoutId = setTimeout(function():void {_innerTask.onThrow(new Error(), this);}, 100);
}
}
public function cancel():void {
_innerTask.cancel();
clearTimeout(_timeoutId);
}
}
```
## Asynchronous concurrence
Sometimes we need to execute asynchronous tasks together and continue when all or any of them are complete.
### Continue when all complete
`asyncAll` function allows to define asynchronous concurrence that ends when _all_ of the given tasks are complete. It returns an array of values _in order of completion of child tasks_.
```actionscript
async(asyncAll(async(new TimeoutTask(200)
.then("second")))
.and(async(new TimeoutTask(100)
.then("first"))));
.then(function(args:*):void {trace(args);}) // first, second
.await();
```
### Continue when any complete
`asyncAny` function allows to define asynchronous concurrence that ends when _any_ of the given tasks is complete. It returns the value of the first complete task. Other child tasks are _canceled_.
```actionscript
async(asyncAny(async(new TimeoutTask(200)
.then("second")))
.or(async(new TimeoutTask(100)
.then("first"))));
.then(function(args:*):void {trace(args);}) // first
.await();
```