Reputation: 6593
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
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