views:

431

answers:

5

Hi All,

I'm calling a function and adding a listener for when the function returns some data. when the data is returned i need to call another function and so on.

Is there an easy way to 'chain' these functions together so that the first one fires - waits for the listener then fires the second one creating a listener for that and so on until the last one calls a separate function that is defined at the start. It would i suppose work on the same lines as the bulk loader scripts.

I'm envisaging the code working something like this:

var dataLoader:DataLoader = new DataLoader(onAllComplete, onError);

dataLoader.add(getData1, {args1, args2}, listnerType.DATA_LOADED);
dataLoader.add(getData2, {args3, args4}, listnerType.DATA_LOADED);
dataLoader.add(getData3, {args5, args6}, listnerType.DATA_LOADED);

dataLoader.start();

private function onAllComplete(e:Array):void {
  //where e contains an array of all the event results
}
private function onError(e:Event):void {
  //will fire if there are any errors along the way
}

Thanks, Josh

A: 

I might be misunderstanding your intentions, but from what you described, you can chain them when the handlers are called... like: Did you want to do something like this but with more condensed syntax?

private function onComplete1(event:Event):void
{
    dataLoader.removeEventListener("onComplete", onComplete1);
    dataLoader.addEventListener("onComplete", onComplete2);

    ... // process event

    dataLoader.load(); // I don't remember the exact calls...
}

private function onComplete2(event:Event)void
{
    dataLoader.removeEventListener("onComplete", onComplete1);
    dataLoader.addEventListener("onComplete", onComplete2);

    ... // process event

    dataLoader.load(); // I don't remember the exact calls...
}
CookieOfFortune
yes i want to do this but condensed! when loading one or two this works fine but i've got situations when i need to load different combinations of data and so it gets messy
Josh
A: 

That interface looks good to me. The implementation depends on how the functions return their data. AS3 does not support threads, so you have to write your getData() functions to run asynchronously. If you're loading data from a remote site or something this is easy, just use the built-in loader functions, use getBytesLoaded() to tell when they're done, and call your OnComplete callback when everyone's loaded.

If you're just doing a really long computation, you're going to have to break it up. Something like:

class Computation {
    function Step() {/* ... */}
    function IsFinished() {/* ... */}
    function GetResult() {/* ... */}
}

Subclass something like that for each kind of computation you need to do, then pass instances into your data loader. Have them Step() once per frame, and call the OnComplete callback when they're all finished.

David Seiler
+2  A: 

I would just do something simple like: (also, this is sorta psuedo code, you'll need the correct error events and stuff)

var numLoaded:int = 0;
var numError:int = 0;
var loadingIndex:int = 0;

var itemsToLoad:Array = ['img1.jpg', 'img2.jpg', 'img3.jpg'];

public function startLoading():void{
     loader.load(itemsToLoad[loadingIndex];
     loader.addEventListener(Event.COMPLETE, completeHandler);
}

public function completeHandler(event:Event):void{
     loadingIndex++;
     numLoaded++;
     if(numLoaded + numError >= itemsToLoad.length){
          onAllItemsComplete();
     }else{
          loader.load(itemsToLoad[loadingIndex];
     }
}

public function errorHandler(event:Event):void{
     loadingIndex++;
     numError++;
     if(numLoaded + numError >= itemsToLoad.length){
          onAllItemsComplete();
     }else{
        loader.load(itemsToLoad[loadingIndex]; 
     }
}
quoo
A: 

splinklibrary contains classes that deal with chained asyncronous operations quite nicely.

maxmc
+1  A: 

Been there, done that. Here's the AS3 code:

package com.vpg.rns.util {

    public class AsynchIterator {
     private var iteratorPosition:int;
     private var iterableObjects:Array;
     private var onApply:Function;
     private var onComplete:Function;
     private var done:Boolean;

     public function get position() { return iteratorPosition; }

     public function get isDone() { return done; }

     public function get size() { return iterableObjects.length; }

     /** Create an iterator that will call the given function repeatCall once for each object in iterableObjects, before finally calling completeCall once at the end.
      * The calls will be made asynchronously, with event handlers used to stitch it all together.
      *
      * @param iterableObjects ....... Every object in this array will be passed as the first argument to repeatCall, in order.
      * @param repeatCall ............ This function will be called once for each object in iterableObjects. Its signature is repeatCall(Object, Function).
      * @param completeCall .......... Called once after every item in the array has been processed.
      *
      *
      */
     public function AsynchIterator(iterableObjects:Array, repeatCall:Function, completeCall:Function) {
      this.iteratorPosition = 0; 
      this.iterableObjects = iterableObjects;
      this.onApply = repeatCall;
      this.onComplete = completeCall;
      this.done = false;
     }

     public function iterate():void {
      doNext();
     }

     private function doNext() {
      if (isDone) {
       // Do nothing - already called onComplete. 
      }
      else if (position == size) { 
       done = true;
       onComplete();
      }
      else {
       var obj:Object = iterableObjects[iteratorPosition++];
       onApply(obj, doNext);
      }
     }

    }

}

Obviously, you will want to add an error handler function, keep track of which failed and succeeded, add options for fast-fail versus do-all-you-can, etc.

  • Paul
Paul Chernoch