pondermatic
pondermatic

Reputation: 6593

Collecting and identifying functions within an array in actionscript

So, I want to do something where I collect functions to be invoked later when a certain condition is met. E.g.

function doSomething(someArg:Object):void {
    if (conditionIsFalse){
        operationsToDoWhenConditionIsTrue.push(function(){
           doSomething(someArg);
        });
    }
}

function onConditionBecomingTrue():void {
    while (operationsToDoWhenConditionIsTrue.length > 0){
        operationsToDoWhenConditionIsTrue.shift()();
    }    
}

So far so good. However at some point I want to iterate over the operationsToDoWhenConditionIsTrue and identify and replace a function. In pseudo code inside the doSomething method it would be:

function doSomething(someArg:Object):void {
   if (conditionIsFalse){
      for (var i:int = 0; i<operationsToDoWhenConditionIsTrue; i++){
         // pseudo code here
         if (typeof operationsToDoWhenConditionIsTrue[i] == doSomething){
             operationsToDoWhenConditionIsTrue[i] = doSomething(someArg);
         }
      }
   }
}

Basically if doSomething is called twice, I only want operationsToDoWhenConditionIsTrue to hold the most recent invocation. Obviously since the invocations are wrapped in function(){} all the functions are the same. Is there any way I can accomplish what I want?

Upvotes: 2

Views: 49

Answers (1)

outis
outis

Reputation: 77450

Create an identifier function that can identify the operations you want to detect as the same. Assign the ID as a property of the anonymous function that you add to the queue. When you iterate over the queue, record IDs in a collection. Don't execute the operation if it's already in the collection.

function doSomething(someArg:Object):void {
    if (conditionIsFalse){
        var operation = function(){
           doSomething(someArg);
        };
        operation.operationID = objID(...);
        operationsToDoWhenConditionIsTrue.push(operation);
    }
}

function onConditionBecomingTrue():void {
    var done = {}, f;
    while (operationsToDoWhenConditionIsTrue.length > 0){
        f = operationsToDoWhenConditionIsTrue.shift();
        if (! f.operationID in done) {
            done[f.operationID] = true;
            f()
        }
    }    
}

As a more efficient variant of this, you can index the queue by IDs, so that a function can be added only once to the queue. However, you will lose control over the order that operations are executed.

var operationsToDoWhenConditionIsTrue:Object = {};

function doSomething(someArg:Object):void {
    if (conditionIsFalse){
        operation.operationID = ...;
        operationsToDoWhenConditionIsTrue[objID(...)] = function(){
           doSomething(someArg);
        };
    }
}

function onConditionBecomingTrue():void {
    for (id in operationsToDoWhenConditionIsTrue){
        operationsToDoWhenConditionIsTrue[id]();
    }
    operationsToDoWhenConditionIsTrue = {};
}

If you need to preserve the sequence (so that operations are executed in order they were first added to the queue, or in the order they were last added), create a queue type that can be indexed by both ID and sequence, which you can do by storing mappings between the index types. Note: the following is untested.

class OperationQueue {
    protected var queue:Array = [];
    protected var idToSeq:Object = {};

    public function push(op:Function):void {
        /* Orders by last insertion. To order by first insertion,
           simply return if the operation is already in the queue.
         */
        if (op.id in this.idToSeq) {
            var seq = this.idToSeq[op.id];
            delete this.queue[seq];
        }
        this.queue.push(op);
        this.idToSeq[op.id] = this.queue.length-1;
    }
    public function runAll():void {
        var op;
        while (this.queue.length > 0) {
            if ((op = this.queue.shift())) {
                delete this.idToSeq[op.id];
                op();
            }
        }
    }
}

Upvotes: 1

Related Questions