I have a weird quirk in ActionScript. I need to pass the index to a callback function.
Here is my code
for (var i:Number = 0; ((i < arrayQueue.length) && uploading); i++)
{
var lid:ListItemData=ListItemData(arrayQueue[i]);
var localI:Number= new Number(i); // to copy?
var errorCallback:Function = function():void { OnUploadError(localI); };
var progressCallback:Function = function(e:ProgressEvent):void { lid.progress = e; OnUploadProgress(localI); };
var completeCallback:Function = function():void { Alert.show('callback'+localI.toString()); OnUploadComplete(localI); }; // localI == arrayQueue.length - 1 (when called)
Alert.show(localI.toString()); // shows current i as expected
lid.fileRef.addEventListener(Event.COMPLETE, completeCallback);
lid.fileRef.addEventListener(ProgressEvent.PROGRESS, progressCallback);
lid.fileRef.addEventListener(HTTPStatusEvent.HTTP_STATUS, errorCallback);
lid.fileRef.addEventListener(IOErrorEvent.IO_ERROR, errorCallback);
lid.fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorCallback);
lid.fileRef.upload(url, 'File');
}
Any idea on how to pass in the index to my callbacks? .upload
does not block.
-
Passing additional parameters for your callbacks is possible via some kind of delegate function or closure. However it is often considered a bad practice. You may use event
target
property instead to determine your index based onFileReference
.Edit: here is a sample of using closures:
function getTimerClosure(ind : int) : Function { return function(event : TimerEvent) { trace(ind); }; } for (var i = 0; i < 10; i++) { var tm : Timer = new Timer(100*i+1, 1); tm.addEventListener(TimerEvent.TIMER, getTimerClosure(i)); tm.start(); }
This will continuously trace numbers from 0 to 9.
Edit2: here is a sample of creating a delegate based on a function closure:
function timerHandler(event : Event, ...rest) : void { trace(event, rest); } function Delegate(scope : Object, func : Function, ...rest) : Function { return function(...args) : void { func.apply(scope, args.concat(rest)); } } var tm : Timer = new Timer(1000, 1); tm.addEventListener(TimerEvent.TIMER, Delegate(this, this.timerHandler, 1, 2, 3)); tm.start();
However this is a bad approach since unsubscribing for such a listener is a hell pain. This in turn will probably cause some memory leakages, which will decrease overall performance of your application. So, use with caution!
Bottom line: if you know how to work with closures, use them - it is a wonderful thing! If you don't care about your application performance in a long perspective, use closures - it's simple!
But if you are unsure about closures, use a more conventional approach. E.g. in your case you could create a
Dictionary
that matches yourFileReference
objects to appropriate indices. Something like that:var frToInd : Dictionary = new Dictionary(false); // false here wouldn't prevent garbage collection of FileReference objects for (var i : int = 0; i < 10; i++) { // blah-blah stuff with `lib` objects frToInd[lib.fileRef] = i; // another weird stuff and subscription } function eventListener(event : Event) : void { // in the event listener just look up target in the dictionary if (frToInd[event.target]) { var ind : int = frToInd[event.target]; } else { // Shouldn't happen since all FileReferences should be in // the Dictionary. But if this happens - it's an error. } }
-- Happy coding!
Daniel A. White : The `target` won't work for me because `target` will only get me the `FileReference`, which is not what I need.dragonfly : But the `FileReference`s for different `lid`s are also different. At least you could store them in an Array and find necessary one in the callback.dragonfly : Also I can bring up the delegate or closure sample. But it's a bad idea to use one.Chetan Sastry : Why is it a bad idea to use the closures here? I find your example perfectly fine.dragonfly : @Daniel, @Chetan: I added some explanation, delegate creation sample and proper solution to the question. -
I have a weird quirk in ActionScript
It's not a quirk, it's variable scope. You should read this article: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9d.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f8c
And you really shouldn't use anonymous, it just makes everything more confusing. You're actually making multiple copies of the same object.
If the arrayQueue is in scope, you can use this code to get the index:
GetArrayIndex(e.currentTarget); function GetArrayIndex(object:Object):Number { for(var i:Number = 0; 0 < arrayQueue.length; i++) { if(object === arrayQueue[i]) return i; } }
You should consider using an uint for the index.
0 comments:
Post a Comment