I finally got fed up enough with Promises et. al. that I broke down and wrote some Async classes which (IMO) are much easier to use.
AsyncTask needs to be subclassed. Here’s an implementation that I’m using in my app. If I find some time, I might create some HTTP tasks. I someone who’s better at docs than me wants to write something, that would be great. ;-) package com.printui.utils { import com.printui.model.vos.FontVO; import com.printui.model.proxies.FontProxy; public class LoadFontTask extends AsyncTask { public function LoadFontTask(font:FontVO) { super(); this.font = font; } private var font:FontVO; override public function run(data:Object = null):void{ var fp:FontProxy = ApplicationFacade.getInstance().retrieveProxy(FontProxy.NAME) as FontProxy; fp.loadNow(font,fontLoadDone); } private function fontLoadDone():void{ if(font.embedded){ complete(); } else { fail(); } } } } I’m using it like so: public function embedFonts(callback:Function):void{ if(fontsEmbedded()){ callback(); } else { var tasks:Array = []; var font:FontVO = getAppliedFont(); if(!font.embedded){ tasks.push(new LoadFontTask(font)); } font = getBulletFont(); if(font && !font.embedded){ tasks.push(new LoadFontTask(font)); } var task:CompoundAsyncTask = new CompoundAsyncTask(tasks); task.done(function(task:AsyncTask):void{ if(task.status == "complete"){ // we're good callback(); } else { // status is "failed" -- not sure what to do... } }); task.run(); } } > On Jun 4, 2019, at 2:55 PM, ha...@apache.org wrote: > > This is an automated email from the ASF dual-hosted git repository. > > harbs pushed a commit to branch develop > in repository https://gitbox.apache.org/repos/asf/royale-asjs.git > > > The following commit(s) were added to refs/heads/develop by this push: > new edb052c Added Async tasks > edb052c is described below > > commit edb052cbf2fb1b9db276dff6e4a94ce0a7679f00 > Author: Harbs <ha...@in-tools.com> > AuthorDate: Tue Jun 4 14:55:00 2019 +0300 > > Added Async tasks > --- > .../CoreJS/src/main/config/compile-js-config.xml | 1 + > .../Core/src/main/config/compile-swf-config.xml | 1 + > .../Core/src/main/resources/basic-manifest.xml | 3 + > .../projects/Core/src/main/royale/CoreClasses.as | 3 + > .../royale/org/apache/royale/utils/ObjectMap.as | 12 ++ > .../org/apache/royale/utils/async/AsyncTask.as | 180 +++++++++++++++++++++ > .../apache/royale/utils/async/CompoundAsyncTask.as | 135 ++++++++++++++++ > .../royale/utils/async/SequentialAsyncTask.as | 77 +++++++++ > 8 files changed, 412 insertions(+) > > diff --git > a/frameworks/js/projects/CoreJS/src/main/config/compile-js-config.xml > b/frameworks/js/projects/CoreJS/src/main/config/compile-js-config.xml > index bdc495e..e912e57 100644 > --- a/frameworks/js/projects/CoreJS/src/main/config/compile-js-config.xml > +++ b/frameworks/js/projects/CoreJS/src/main/config/compile-js-config.xml > @@ -71,6 +71,7 @@ > </source-path> > > <warn-no-constructor>false</warn-no-constructor> > + <allow-abstract-classes>true</allow-abstract-classes> > > <!-- Use of the instanceof operator. --> > <warn-instance-of-changes>false</warn-instance-of-changes> > diff --git a/frameworks/projects/Core/src/main/config/compile-swf-config.xml > b/frameworks/projects/Core/src/main/config/compile-swf-config.xml > index ecd4c77..265d4d8 100644 > --- a/frameworks/projects/Core/src/main/config/compile-swf-config.xml > +++ b/frameworks/projects/Core/src/main/config/compile-swf-config.xml > @@ -75,6 +75,7 @@ > </source-path> > > <warn-no-constructor>false</warn-no-constructor> > + <allow-abstract-classes>true</allow-abstract-classes> > > <!-- Use of the instanceof operator. --> > <warn-instance-of-changes>false</warn-instance-of-changes> > diff --git a/frameworks/projects/Core/src/main/resources/basic-manifest.xml > b/frameworks/projects/Core/src/main/resources/basic-manifest.xml > index ae4b7f7..f40d2e4 100644 > --- a/frameworks/projects/Core/src/main/resources/basic-manifest.xml > +++ b/frameworks/projects/Core/src/main/resources/basic-manifest.xml > @@ -52,4 +52,7 @@ > <component id="StyleChangeNotifier" > class="org.apache.royale.core.StyleChangeNotifier"/> > > <component id="State" class="org.apache.royale.states.State"/> > + > + <component id="CompoundAsyncTask" > class="org.apache.royale.utils.CompoundAsyncTask"> > + <component id="SequentialAsyncTask" > class="org.apache.royale.utils.SequentialAsyncTask"> > </componentPackage> > diff --git a/frameworks/projects/Core/src/main/royale/CoreClasses.as > b/frameworks/projects/Core/src/main/royale/CoreClasses.as > index 678df00..cdd9b25 100644 > --- a/frameworks/projects/Core/src/main/royale/CoreClasses.as > +++ b/frameworks/projects/Core/src/main/royale/CoreClasses.as > @@ -308,6 +308,9 @@ internal class CoreClasses > import org.apache.royale.utils.date.addSeconds; addSeconds; > import org.apache.royale.utils.date.addYears; addYears; > > + import org.apache.royale.utils.async.CompoundAsyncTask; > CompoundAsyncTask; > + import org.apache.royale.utils.async.SequentialAsyncTask; > SequentialAsyncTask; > + > import org.apache.royale.utils.css.addDynamicSelector; > addDynamicSelector; > > COMPILE::JS > diff --git > a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/ObjectMap.as > > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/ObjectMap.as > index d3bd5bc..836b76e 100644 > --- > a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/ObjectMap.as > +++ > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/ObjectMap.as > @@ -157,9 +157,21 @@ package org.apache.royale.utils > > COMPILE::JS > { > + /** > + * @royalesuppresspublicvarwarning > + */ > public var get:Function = objectGet; > + /** > + * @royalesuppresspublicvarwarning > + */ > public var set:Function = objectSet; > + /** > + * @royalesuppresspublicvarwarning > + */ > public var has:Function = objectHas; > + /** > + * @royalesuppresspublicvarwarning > + */ > public var delete:Function = objectDelete; > } > > diff --git > a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/AsyncTask.as > > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/AsyncTask.as > new file mode 100644 > index 0000000..44a31ce > --- /dev/null > +++ > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/AsyncTask.as > @@ -0,0 +1,180 @@ > +//////////////////////////////////////////////////////////////////////////////// > +// > +// Licensed to the Apache Software Foundation (ASF) under one or more > +// contributor license agreements. See the NOTICE file distributed with > +// this work for additional information regarding copyright ownership. > +// The ASF licenses this file to You under the Apache License, Version 2.0 > +// (the "License"); you may not use this file except in compliance with > +// the License. You may obtain a copy of the License at > +// > +// http://www.apache.org/licenses/LICENSE-2.0 > +// > +// Unless required by applicable law or agreed to in writing, software > +// distributed under the License is distributed on an "AS IS" BASIS, > +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > +// See the License for the specific language governing permissions and > +// limitations under the License. > +// > +//////////////////////////////////////////////////////////////////////////////// > +package org.apache.royale.utils.async > +{ > + import org.apache.royale.events.EventDispatcher; > + import org.apache.royale.events.Event; > + > + /** > + * AsyncTask is a base class for AsyncTasks which let the caller know when > they are done. > + * AsyncTask is an OOP replacement for Promises and simple callbacks which > allows for > + * strongly typed async requests with any kind of payload and behavior. > + * AsyncTask must be subclassed to be used. > + * The subclass must implement the `run` method to define the behavior > when the task is "run". > + */ > + > + [Event(name="complete", type="org.apache.royale.events.Event")] > + [Event(name="failed", type="org.apache.royale.events.Event")] > + [Event(name="done", type="org.apache.royale.events.Event")] > + public abstract class AsyncTask extends EventDispatcher > + { > + public function AsyncTask() > + { > + > + } > + public static const INITIALIZED:String = "initialized"; > + public static const PENDING:String = "pending"; > + public static const COMPLETE:String = "complete"; > + public static const CANCELED:String = "canceled"; > + public static const FAILED:String = "failed"; > + /** > + * Used in compound tasks > + */ > + public static const MIXED:String = "mixed"; > + protected var _status:String = "initialized"; > + /** > + * One of: initialized, pending, complete, failed or mixed (for compound > tasks) > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function get status():String > + { > + return _status; > + } > + > + /** > + * completed (and a status of `complete`) means the task completed > successfully > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function get completed():Boolean > + { > + return _status == "complete"; > + } > + public function set completed(value:Boolean):void > + { > + _status = "complete"; > + } > + > + /** > + * failed (and a status of `failed`) means the task resolved to a failed > state > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function get failed():Boolean > + { > + return _status == "failed"; > + } > + public function set failed(value:Boolean):void > + { > + _status = "failed"; > + } > + /** > + * resolves the task as complete > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function complete():void{ > + _status = "complete"; > + dispatchEvent(new Event("complete")); > + notifyDone(); > + } > + /** > + * Resolves the task as failed > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function fail():void{ > + _status = "failed"; > + dispatchEvent(new Event("failed")); > + notifyDone(); > + } > + protected function notifyDone():void{ > + dispatchEvent(new Event("done")); > + if(!doneCallbacks){ > + return; > + } > + for(var i:int=0;i<doneCallbacks.length;i++){ > + doneCallbacks[i](this); > + } > + } > + private var doneCallbacks:Array; > + > + /** > + * done accepts a callback which is called when the task is resolved. > + * The callback is resolved whether the task is successfully completed > or not. > + * The properties of the task should be examined in the callback to > determine the results. > + * The `done` event can be listened too as well. > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function done(callback:Function):AsyncTask{ > + if(!doneCallbacks){ > + doneCallbacks = []; > + } > + doneCallbacks.push(callback); > + return this; > + } > + public abstract function run(data:Object=null):void; > + > + /** > + * cancel resolves the task as "canceled" > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function cancel():void > + { > + _status = "canceled"; > + notifyDone(); > + } > + > + private var _data:Object; > + /** > + * The data of the task > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function get data():Object > + { > + return _data; > + } > + > + public function set data(value:Object):void > + { > + _data = value; > + } > + > + } > +} > \ No newline at end of file > diff --git > a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/CompoundAsyncTask.as > > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/CompoundAsyncTask.as > new file mode 100644 > index 0000000..3dee5ef > --- /dev/null > +++ > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/CompoundAsyncTask.as > @@ -0,0 +1,135 @@ > +//////////////////////////////////////////////////////////////////////////////// > +// > +// Licensed to the Apache Software Foundation (ASF) under one or more > +// contributor license agreements. See the NOTICE file distributed with > +// this work for additional information regarding copyright ownership. > +// The ASF licenses this file to You under the Apache License, Version 2.0 > +// (the "License"); you may not use this file except in compliance with > +// the License. You may obtain a copy of the License at > +// > +// http://www.apache.org/licenses/LICENSE-2.0 > +// > +// Unless required by applicable law or agreed to in writing, software > +// distributed under the License is distributed on an "AS IS" BASIS, > +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > +// See the License for the specific language governing permissions and > +// limitations under the License. > +// > +//////////////////////////////////////////////////////////////////////////////// > +package org.apache.royale.utils.async > +{ > + import org.apache.royale.events.Event; > + > + /** > + * The CompoundAsyncTask class allows running a number of AsyncTasks in > parallel and resolves when they are done. > + */ > + public class CompoundAsyncTask extends AsyncTask > + { > + public function CompoundAsyncTask(tasks:Array=null) > + { > + super(); > + if(!tasks){ > + tasks = []; > + } > + this.tasks = tasks; > + completedTasks = []; > + failedTasks = []; > + } > + protected var tasks:Array; > + > + private var _failEarly:Boolean; > + /** > + * If <code>failEarly</code> is true, the task will fail as soon as the > first subtask fails. > + */ > + public function get failEarly():Boolean > + { > + return _failEarly; > + } > + > + public function set failEarly(value:Boolean):void > + { > + _failEarly = value; > + } > + > + public function addTask(task:AsyncTask):void{ > + tasks.push(task); > + } > + > + protected var pendingTasks:Array; > + > + /** > + * @royalesuppresspublicvarwarning > + */ > + public var completedTasks:Array; > + /** > + * @royalesuppresspublicvarwarning > + */ > + public var failedTasks:Array; > + > + override public function run(data:Object=null):void > + { > + if(_status == "pending"){// don't allow running twice > + return; > + } > + _status = "pending"; > + pendingTasks = []; > + for(var i:int=0;i<tasks.length;i++){ > + var task:AsyncTask = tasks[i]; > + task.done(handleDone); > + pendingTasks.push(task); > + task.run(); > + } > + } > + private function handleDone(task:AsyncTask):void > + { > + if(_status != "pending") > + { > + return; > + } > + var idx:int = pendingTasks.indexOf(task); > + pendingTasks.splice(idx,1); > + switch(task.status){ > + case "complete": > + completedTasks.push(task); > + break; > + case "failed": > + failedTasks.push(task); > + if(failEarly) > + { > + while(pendingTasks.length) > + { > + var pending:AsyncTask = pendingTasks.pop(); > + pending.cancel(); > + } > + fail(); > + return; > + } > + break; > + default:// not sure why this would happen > + throw new Error("Unknown task status"); > + } > + if(pendingTasks.length == 0) > + { > + setFinalStatus(); > + } > + } > + protected function setFinalStatus():void > + { > + if(failedTasks.length == 0) > + { > + complete(); > + } > + else if(completedTasks.length == 0) > + { > + fail(); > + } > + else > + {// Some passed and some failed -- Does this make sense? > + _status = "mixed"; > + dispatchEvent(new Event("failed")); > + dispatchEvent(new Event("complete")); > + notifyDone(); > + } > + } > + } > +} > \ No newline at end of file > diff --git > a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/SequentialAsyncTask.as > > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/SequentialAsyncTask.as > new file mode 100644 > index 0000000..77f2f18 > --- /dev/null > +++ > b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/async/SequentialAsyncTask.as > @@ -0,0 +1,77 @@ > +//////////////////////////////////////////////////////////////////////////////// > +// > +// Licensed to the Apache Software Foundation (ASF) under one or more > +// contributor license agreements. See the NOTICE file distributed with > +// this work for additional information regarding copyright ownership. > +// The ASF licenses this file to You under the Apache License, Version 2.0 > +// (the "License"); you may not use this file except in compliance with > +// the License. You may obtain a copy of the License at > +// > +// http://www.apache.org/licenses/LICENSE-2.0 > +// > +// Unless required by applicable law or agreed to in writing, software > +// distributed under the License is distributed on an "AS IS" BASIS, > +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > +// See the License for the specific language governing permissions and > +// limitations under the License. > +// > +//////////////////////////////////////////////////////////////////////////////// > +package org.apache.royale.utils.async > +{ > + /** > + * The SequentialAsyncTask runs a list of tasks in sequential order. > + * Each sunsequent task is only run once the previous task is done. > + * The previous task is used as the argument for the next task's run > method. > + * This enables the chaining of results. > + */ > + public class SequentialAsyncTask extends CompoundAsyncTask > + { > + /** > + * @langversion 3.0 > + * @playerversion Flash 10.2 > + * @playerversion AIR 2.6 > + * @productversion Royale 0.9.6 > + */ > + public function SequentialAsyncTask(tasks:Array=null) > + { > + super(tasks); > + } > + override public function run(data:Object=null):void > + { > + _status = "pending"; > + pendingTasks = tasks.slice(); > + var task:AsyncTask = pendingTasks.shift(); > + task.done(handleDone); > + task.run(); > + } > + private function handleDone(task:AsyncTask):void > + { > + if(_status != "pending"){ > + return; > + } > + switch(task.status){ > + case "complete": > + completedTasks.push(task); > + break; > + case "failed": > + failedTasks.push(task); > + if(failEarly){ > + pendingTasks = []; > + fail(); > + return; > + } > + break; > + default:// not sure why this would happen > + throw new Error("Unknown task status"); > + > + } > + if(pendingTasks.length){ > + var nextTask:AsyncTask = pendingTasks.shift(); > + nextTask.done(handleDone); > + nextTask.run(task); > + } else { > + setFinalStatus(); > + } > + } > + } > +} > \ No newline at end of file >