I'm rusty with delegates and closures in JavaScript, and think I came across a situation where I'd like to try to use one or both.
I have a web app that behaves a lot like a forms app, with fields hitting a server to change data on every onBlur or onChange (depending on the form element). I use ASP.NET 3.5's Web Services and jQuery to do most of the work.
What you need to know for the example:
isBlocking() is a simple mechanism to form some functions to be synchronous (like a mutex)
isDirty(el) checks to make sure the value of the element actually changed before wasting a call to the server
Agent() returns a singleton instance of the WebService proxy class
getApplicationState() passes a base-64 encoded string to the web service. This string represents the state of the application -- the value of the element and the state are passed to a service that does some calculations. The onSuccess function of the web service call returns the new state, which the client processes and updates the entire screen.
waitForCallback() sets a flag that isBlocking() checks for the mutex
Here's an example of one of about 50 very similar functions:
function Field1_Changed(el) {
if (isBlocking()) return false;
if (isDirty(el)) {
Agent().Field1_Changed($j(el).val(), getApplicationState());
waitForCallback();
}
}
The big problem is that the Agent().Field_X_Changed methods can accept a different number of parameters, but it's usually just the value and the state. So, writing these functions gets repetitive. I have done this so far to try out using delegates:
function Field_Changed(el, updateFunction, checkForDirty) {
if (isBlocking()) return false;
var isDirty = true; // assume true
if (checkForDirty === true) {
isDirty = IsDirty(el);
}
if (isDirty) {
updateFunction(el);
waitForCallback();
}
}
function Field1_Changed(el) {
Field_Changed(el, function(el) {
Agent().Field1_Changed($j(el).val(), getTransactionState());
}, true);
}
This is ok, but sometimes I could have many parameters:
...
Agent().Field2_Changed($j(el).val(), index, count, getApplicationState());
....
What I'd ultimately like to do is make one-linen calls, something like this (notice no getTransactionState() calls -- I would like that automated somehow):
// Typical case: 1 value parameter
function Field1_Changed(el) {
Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val()), true);
}
// Rare case: multiple value parameters
function Field2_Changed(el, index, count) {
Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val(), index, count), true);
}
function Field_Changed(el, theDelegate, checkIsDirty) {
???
}
function delegate(method) {
/* create the change delegate */
???
}
Ok, my first question is: Is this all worth it? Is this harder to read but easier to maintain or the other way around? This is a pretty good undertaking, so I may end up putting a bounty on this one, but I'd appreciate any help you could offer. Thanks!
UPDATE
So, I've accepted an answer based on the fact that it pointed me in the right direction. I thought I'd come back and post my solution so that others who may just be starting out with delegates have something to model from. I'm also posting it to see if anybody wants to try an optimize it or make suggestions. Here's the common Field_Changed() method I came up with, with checkForDirty and omitState being optional parameters:
function Field_Changed(el, args, delegate, checkForDirty, omitState) {
if (isBlocking()) return false;
if (!$j.isArray(args) || args.length == 0) {
alert('The "args" parameter in Field_Changed() must be an array.');
return false;
}
checkForDirty = checkForDirty || true; // assume true if not passed
var isDirty = true; // assume true for updates that don't require this check
if (checkForDirty === true) {
isDirty = fieldIsDirty(el);
}
if (isDirty) {
omitState = omitState || false; // assume false if not passed
if (!omitState) {
var state = getTransactionState();
args.push(state);
}
delegate.apply(this, args);
waitForCallback();
}
}
It handles everything I need it to (check for dirty, applying the application state when I need it to, and forcing synchronous webservice calls. I use it like this:
function TransactionAmount_Changed(el) {
Field_Changed(el, [cleanDigits($j(el).val())], Agent().TransactionAmount_Changed, true);
}
cleanDigits strips out junk characters the user may have tried to type in. So, thanks to everyone, and happy coding!
OK, few things:
Delegates are extremely simple in javascript since functions are first class members.
Function.apply lets you call a function with an array of arguments.
So you can write it this way
function Field_Changed(delegate, args)
{
if (isBlocking()) return false;
if (isDirty(args[0])) { //args[0] is el
delegate.apply(this, args);
waitForCallback();
}
}
And call it as:
Field_Changed(Agent().Field2_Changed, [el, getApplicationState(), whatever...]);
I have been using the following utility function that I wrote a long time ago:
/**
* #classDescription This class contains different utility functions
*/
function Utils()
{}
/**
* This method returns a delegate function closure that will call
* targetMethod on targetObject with specified arguments and with
* arguments specified by the caller of this delegate
*
* #param {Object} targetObj - the object to call the method on
* #param {Object} targetMethod - the method to call on the object
* #param {Object} [arg1] - optional argument 1
* #param {Object} [arg2] - optional argument 2
* #param {Object} [arg3] - optional argument 3
*/
Utils.createDelegate = function( targetObj, targetMethod, arg1, arg2, arg3 )
{
// Create an array containing the arguments
var initArgs = new Array();
// Skip the first two arguments as they are the target object and method
for( var i = 2; i < arguments.length; ++i )
{
initArgs.push( arguments[i] );
}
// Return the closure
return function()
{
// Add the initial arguments of the delegate
var args = initArgs.slice(0);
// Add the actual arguments specified by the call to this list
for( var i = 0; i < arguments.length; ++i )
{
args.push( arguments[i] );
}
return targetMethod.apply( targetObj, args );
};
}
So, in your example, I would replace
function Field1_Changed(el) {
Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val()), true);
}
With something along the lines
function Field1_Changed(el) {
Field_Changed(el, Utils.createDelegate(Agent(), Agent().Field1_Changed, $j(el).val()), true);
}
Then, inside of Agent().FieldX_Changed I would manually call getApplicationState() (and encapsulate that logic into a generic method to process field changes that all of the Agent().FieldX_Changed methods would internally call).
Closures and delegates in JavaScript:
http://www.terrainformatica.com/2006/08/delegates-in-javascript/
http://www.terrainformatica.com/2006/08/delegates-in-javascript-now-with-parameters/
Related
I'm not sure of the best approach for handling scoping of "this" in TypeScript.
Here's an example of a common pattern in the code I am converting over to TypeScript:
class DemonstrateScopingProblems {
private status = "blah";
public run() {
alert(this.status);
}
}
var thisTest = new DemonstrateScopingProblems();
// works as expected, displays "blah":
thisTest.run();
// doesn't work; this is scoped to be the document so this.status is undefined:
$(document).ready(thisTest.run);
Now, I could change the call to...
$(document).ready(thisTest.run.bind(thisTest));
...which does work. But it's kinda horrible. It means that code can all compile and work fine in some circumstances, but if we forget to bind the scope it will break.
I would like a way to do it within the class, so that when using the class we don't need to worry about what "this" is scoped to.
Any suggestions?
Update
Another approach that works is using the fat arrow:
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
alert(this.status);
}
}
Is that a valid approach?
You have a few options here, each with its own trade-offs. Unfortunately there is no obvious best solution and it will really depend on the application.
Automatic Class Binding
As shown in your question:
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
alert(this.status);
}
}
Good/bad: This creates an additional closure per method per instance of your class. If this method is usually only used in regular method calls, this is overkill. However, if it's used a lot in callback positions, it's more efficient for the class instance to capture the this context instead of each call site creating a new closure upon invoke.
Good: Impossible for external callers to forget to handle this context
Good: Typesafe in TypeScript
Good: No extra work if the function has parameters
Bad: Derived classes can't call base class methods written this way using super.
Bad: The exact semantics of which methods are "pre-bound" and which aren't create an additional non-typesafe contract between your class and its consumers.
Function.bind
Also as shown:
$(document).ready(thisTest.run.bind(thisTest));
Good/bad: Opposite memory/performance trade-off compared to the first method
Good: No extra work if the function has parameters
Bad: In TypeScript, this currently has no type safety
Bad: Only available in ECMAScript 5, if that matters to you
Bad: You have to type the instance name twice
Fat arrow
In TypeScript (shown here with some dummy parameters for explanatory reasons):
$(document).ready((n, m) => thisTest.run(n, m));
Good/bad: Opposite memory/performance trade-off compared to the first method
Good: In TypeScript, this has 100% type safety
Good: Works in ECMAScript 3
Good: You only have to type the instance name once
Bad: You'll have to type the parameters twice
Bad: Doesn't work with variadic parameters
Another solution that requires some initial setup but pays off with its invincibly light, literally one-word syntax is using Method Decorators to JIT-bind methods through getters.
I've created a repo on GitHub to showcase an implementation of this idea (it's a bit lengthy to fit into an answer with its 40 lines of code, including comments), that you would use as simply as:
class DemonstrateScopingProblems {
private status = "blah";
#bound public run() {
alert(this.status);
}
}
I haven't seen this mentioned anywhere yet, but it works flawlessly. Also, there is no notable downside to this approach: the implementation of this decorator -- including some type-checking for runtime type-safety -- is trivial and straightforward, and comes with essentially zero overhead after the initial method call.
The essential part is defining the following getter on the class prototype, which is executed immediately before the first call:
get: function () {
// Create bound override on object instance. This will hide the original method on the prototype, and instead yield a bound version from the
// instance itself. The original method will no longer be accessible. Inside a getter, 'this' will refer to the instance.
var instance = this;
Object.defineProperty(instance, propKey.toString(), {
value: function () {
// This is effectively a lightweight bind() that skips many (here unnecessary) checks found in native implementations.
return originalMethod.apply(instance, arguments);
}
});
// The first invocation (per instance) will return the bound method from here. Subsequent calls will never reach this point, due to the way
// JavaScript runtimes look up properties on objects; the bound method, defined on the instance, will effectively hide it.
return instance[propKey];
}
Full source
The idea can be also taken one step further, by doing this in a class decorator instead, iterating over methods and defining the above property descriptor for each of them in one pass.
Necromancing.
There's an obvious simple solution that doesn't require arrow-functions (arrow-functions are 30% slower), or JIT-methods through getters.
That solution is to bind the this-context in the constructor.
class DemonstrateScopingProblems
{
constructor()
{
this.run = this.run.bind(this);
}
private status = "blah";
public run() {
alert(this.status);
}
}
You can write an autobind method to automatically bind all functions in the constructor of the class:
class DemonstrateScopingProblems
{
constructor()
{
this.autoBind(this);
}
[...]
}
export function autoBind(self)
{
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
const val = self[key];
if (key !== 'constructor' && typeof val === 'function')
{
// console.log(key);
self[key] = val.bind(self);
} // End if (key !== 'constructor' && typeof val === 'function')
} // Next key
return self;
} // End Function autoBind
Note that if you don't put the autobind-function into the same class as a member function, it's just autoBind(this); and not this.autoBind(this);
And also, the above autoBind function is dumbed down, to show the principle.
If you want this to work reliably, you need to test if the function is a getter/setter of a property as well, because otherwise - boom - if your class contains properties, that is.
Like this:
export function autoBind(self)
{
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
if (key !== 'constructor')
{
// console.log(key);
let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
if (!desc.configurable) {
console.log("AUTOBIND-WARNING: Property \"" + key + "\" not configurable ! (" + self.constructor.name + ")");
continue;
}
let g = desc.get != null;
let s = desc.set != null;
if (g || s)
{
var newGetter = null;
var newSetter = null;
if (g)
newGetter = desc.get.bind(self);
if (s)
newSetter = desc.set.bind(self);
if (newGetter != null && newSetter == null) {
Object.defineProperty(self, key, {
get: newGetter,
enumerable: desc.enumerable,
configurable: desc.configurable
});
}
else if (newSetter != null && newGetter == null) {
Object.defineProperty(self, key, {
set: newSetter,
enumerable: desc.enumerable,
configurable: desc.configurable
});
}
else {
Object.defineProperty(self, key, {
get: newGetter,
set: newSetter,
enumerable: desc.enumerable,
configurable: desc.configurable
});
}
continue; // if it's a property, it can't be a function
} // End if (g || s)
} // End if (desc != null)
if (typeof (self[key]) === 'function')
{
let val = self[key];
self[key] = val.bind(self);
} // End if (typeof (self[key]) === 'function')
} // End if (key !== 'constructor')
} // Next key
return self;
} // End Function autoBind
In your code, have you tried just changing the last line as follows?
$(document).ready(() => thisTest.run());
I need to return the value from my Responder object. Right now, I have:
private function pro():int {
gateway.connect('http://10.0.2.2:5000/gateway');
var id:int = 0;
function ret_pr(result:*):int {
return result
}
var responder:Responder = new Responder(ret_pr);
gateway.call('sx.xj', responder);
return id
}
Basically, I need to know how to get the return value of ret_pr into id or anything that I return from that function. The responder just seems to eat it. I can't use a public variable somewhere else because this will be running multiple times at once, so I need local scope.
This is how I'd write a connection to the AMF server, call it and store the resulting value. Remember that the result won't be available instantly so you'll set up the responder to "respond" to the data once it returns from the server.
public function init():void
{
connection = new NetConnection();
connection.connect('http://10.0.2.2:5000/gateway');
setSessionID( 1 );
}
public function setSessionID(user_id:String):void
{
var amfResponder:Responder = new Responder(setSessionIDResult, onFault);
connection.call("ServerService.setSessionID", amfResponder , user_id);
}
private function setSessionIDResult(result:Object):void {
id = result.id;
// here you'd do something to notify that the data has been downloaded. I'll usually
// use a custom Event class that just notifies that the data is ready,but I'd store
// it here in the class with the AMF call to keep all my data in one place.
}
private function onFault(fault:Object):void {
trace("AMFPHP error: "+fault);
}
I hope that can point you in the right direction.
private function pro():int {
gateway.connect('http://10.0.2.2:5000/gateway');
var id:int = 0;
function ret_pr(result:*):int {
return result
}
var responder:Responder = new Responder(ret_pr);
gateway.call('sx.xj', responder);
return id
}
This code is never going to get you what you want. You need to use a proper result function. The anonymous function responder return value will not be used by the surrounding function. It will always return 0 in this case. You are dealing with an asynchronous call here, and your logic needs to handle that accordingly.
private function pro():void {
gateway.connect('http://10.0.2.2:5000/gateway');
var responder:Responder = new Responder(handleResponse);
gateway.call('sx.xj', responder);
}
private function handleResponse(result:*):void
{
var event:MyCustomNotificationEvent = new MyCustomNotificationEvent(
MyCustomNotificationEvent.RESULTS_RECEIVED, result);
dispatchEvent(event);
//a listener responds to this and does work on your result
//or maybe here you add the result to an array, or some other
//mechanism
}
The point there being using anon functions/closures isn't going to give you some sort of pseudo-syncronous behavior.
I am trying to iterate through each of the members of an object. For each member, I check to see if it is a function or not. If it is a function, I want to get the name of it and perform some logic based on the name of the function. I don't know if this is even possible though. Is it? Any tips?
example:
var mems: Object = getMemberNames(obj, true);
for each(mem: Object in members) {
if(!(mem is Function))
continue;
var func: Function = Function(mem);
//I want something like this:
if(func.getName().startsWith("xxxx")) {
func.call(...);
}
}
I'm having a hard time finding much on doing this. Thanks for the help.
Your pseudocode is close to doing what you want. Instead of using getMemberNames, however, which can get private methods, you can loop over the members with a simple for..in loop, and get the values of the members using brackets. For example:
public function callxxxxMethods(o:Object):void
{
for(var name:String in o)
{
if(!(o[name] is Function))
continue;
if(name.startsWith("xxxx"))
{
o[name].call(...);
}
}
}
Dan Monego's answer is on the money, but only works for dynamic members. For any fixed instance (or static) members, you'll have to use flash.utils.describeType:
var description:XML = describeType(obj);
/* By using E4X, we can use a sort of lamdba like statement to find the members
* that match our criteria. In this case, we make sure the name starts with "xxx".
*/
var methodNames:XMLList = description..method.(#name.substr(0, 3) == "xxx");
for each (var method:XML in methodNames)
{
var callback:Function = obj[method.#name];
callback(); // For calling with an unknown set of parameters, use callback.apply
}
Use this in conjunction with Dan's answer if you have a mix of dynamic and fixed members.
I've done some work and combined both approaches. Mind you, it works only for publicly visible members - in all other cases null is returned.
/**
* Returns the name of a function. The function must be <b>publicly</b> visible,
* otherwise nothing will be found and <code>null</code> returned.</br>Namespaces like
* <code>internal</code>, <code>protected</code>, <code>private</code>, etc. cannot
* be accessed by this method.
*
* #param f The function you want to get the name of.
*
* #return The name of the function or <code>null</code> if no match was found.</br>
* In that case it is likely that the function is declared
* in the <code>private</code> namespace.
**/
public static function getFunctionName(f:Function):String
{
// get the object that contains the function (this of f)
var t:Object = getSavedThis(f);
// get all methods contained
var methods:XMLList = describeType(t)..method.#name;
for each (var m:String in methods)
{
// return the method name if the thisObject of f (t)
// has a property by that name
// that is not null (null = doesn't exist) and
// is strictly equal to the function we search the name of
if (t.hasOwnProperty(m) && t[m] != null && t[m] === f) return m;
}
// if we arrive here, we haven't found anything...
// maybe the function is declared in the private namespace?
return null;
}
greetz,
tox
This question doesn't relate only to MouseEvent.CLICK event type but to all event types that already exist in AS3. I read a lot about custom events but until now I couldn't figure it out how to do what I want to do. I'm going to try to explain, I hope you understand:
Here is a illustration of my situation:
for(var i:Number; i < 10; i++){
var someVar = i;
myClips[i].addEventListener(MouseEvent.CLICK, doSomething);
}
function doSomething(e:MouseEvent){ /* */ }
But I want to be able to pass someVar as a parameter to doSomething. So I tried this:
for(var i:Number; i < 10; i++){
var someVar = i;
myClips[i].addEventListener(MouseEvent.CLICK, function(){
doSomething(someVar);
});
}
function doSomething(index){ trace(index); }
This kind of works but not as I expect. Due to the function closures, when the MouseEvent.CLICK events are actually fired the for loop is already over and someVar is holding the last value, the number 9 in the example. So every click in each movie clip will call doSomething passing 9 as the parameter. And it's not what I want.
I thought that creating a custom event should work, but then I couldn't find a way to fire a custom event when the MouseEvent.CLICK event is fired and pass the parameter to it. Now I don't know if it is the right answer.
What should I do and how?
You really need to extend the event class to create your own event with extra parameters. Placing functions inside the addEventListener (anonymous functions) is a recipe for memory leaks, which is not good.
Take a look at the following.
import flash.events.Event;
//custom event class to enable the passing of strings along with the actual event
public class TestEvent extends Event
{
public static const TYPE1 :String = "type1";
public static const TYPE2 :String = "type2";
public static const TYPE3 :String = "type3";
public var parameterText:String;
public function TestEvent (type:String, searchText:String)
{
this.parameterText = searchText;
super(type);
}
}
when you create a new event such as
dispatchEvent(new TestEvent(TestEvent.TYPE1, 'thisIsTheParameterText'))" ;
you can then listen for that event like this
someComponent.addEventListener(TestEvent.TYPE1, listenerFunction, true , 0, true);
and inside the function 'listenerFunction' event.parameterText will contain your parameter.
so inside your myClips component you would fire off the custom event and listen for that event and not the Click event.
Without knowing more about your application, it seems more like you should use the target to pass parameters, or extend MouseEvent. The former would be more in line with common practice, though. So for example, if you exposed an integer public property on your "clip" object (whatever it is):
public class MyClip
{
public var myPublicProperty:int;
public function MyClip() { //... }
}
for (var i:int = 0; i < 10; i++)
{
myClips[i].myPublicProperty = i;
myClips[i].addEventListener(MouseEvent.CLICK, doSomething);
}
... and then, in your event listener, you could retrieve that property using either the target or currentTarget property of the event (probably currentTarget, in your case):
function doSomething(event:MouseEvent):void
{
trace(event.currentTarget.myPublicProperty.toString());
}
That ought to do it! Good luck.
private function myCallbackFunction(e:Event, parameter:String):void
{
//voila, here's your parameter
}
private function addArguments(method:Function, additionalArguments:Array):Function
{
return function(event:Event):void {method.apply(null, [event].concat(additionalArguments));}
}
var parameter:String = "A sentence I want to pass along";
movieClip.addEventListener(Event.COMPLETE, addArguments(myCallbackFunction, [parameter] ) );
Take advantage of the dynamic function construction in AS3.
You can accomplish this by getting your handler out of a function that gives the variable closure, like this:
for (var i=0; i<5; i++) {
myClips[i].addEventListener( MouseEvent.CLICK, getHandler(i) );
}
function getHandler(i) {
return function( e:MouseEvent ) {
test(i);
}
}
function test( j ) {
trace("foo "+j);
}
Also, as for why this creates a new closure, you might want to check the explanation in the accepted answer to this similar question.
Thanks so much for this usefull tips, this technique is better to understand than classes explanation.
for me I just started new code algorithm using this technique to solve link relation between timers array and viewports array, and update status by change text inside them frequently, by passing ID's with timers events.
like this:
var view:Object=[];
for(var i:uint=0;i<Camera.names.length;i++){
view[i]=getChildByName("Cam"+i);
//_________ Add Text _________
var tf:TextField = new TextField();
tf.autoSize = TextFieldAutoSize.LEFT;
tf.textColor=0xffffff;
view[i].tf=view[i].addChild(tf);
//_________ Add Timer _________
var t:Timer = new Timer(1000);
view[i].timer=t;
view[i].timer.start();
view[i].timer.addEventListener(TimerEvent.TIMER, addArg(i));
}
function addArg(adi:uint):Function {
return function(event:TimerEvent):void {
updatecamstatus(adi);
}
}
function updatecamstatus(vH:uint):void {
with (view[vH]){
tf.text="Cam" + vH + "\n";
tf.appendText("activityLevel: " + videosource.activityLevel + "\n");
tf.appendText("motionLevel: " + videosource.motionLevel + "\n");
}
}
I see your main goal isn't actually to create a custom MouseEvent.CLICK, but to pass a parameter to the function. You don't need to complicatedly create or extend anything. There's a simple and closure-trouble-free way to do it.
Just make your function like this:
function doSomething(index:Number):Function {
return function(e:MouseEvent):void {
// Use "e" and "index" here. They'll be unique for each addEventListener()
trace(index);
}
}
This technique can relate to any AS3 event type you can use addEventListener on.
And now you can add it like this:
var functionsDoSomething:Object;
for (var i:Number = 0; i < 10; i++) {
var someVar:Number = i;
functionsDoSomething[i] = doSomething(someVar);
myClips[i].addEventListener(MouseEvent.CLICK, functionsDoSomething[i]);
}
The doSomething(someVar) can be used directly on addEventListener(), but it's better to keep it in a variable because you'll be able to remove it later the same fashion you added it:
for (i = 0; i < 10; i++) {
myClips[i].removeEventListener(MouseEvent.CLICK, functionsDoSomething[i]);
}
The commonly used e.currentTarget.someCustomProperty works for dynamic objects (i.e. MovieClip), but will let you down at anything else (i.e. Sprite), forcing you to build a whole custom extended object/event for every type.
This solution deals with every "listenable" object and event. And this answer has more details and examples on it.
With ActionScript3 for Flash Player 9+, what is the nicest way to call a "once-off" function after exactly one frame?
I am aware of the Timer class and it's uses, and the callLater method on Flex UIComponents (which, glancing through the source, doesn't look very efficient in this scenario). I'm also aware of setTimeout (in flash.utils).
The Timer class and setTimeout utility are both time based, so how would we guarantee that our function will get called after exactly one frame?
From my limited testing it seems that functions passed to setTimeout only execute after at least one frame (try setting the delay to 0). But this is not guaranteed.
Of course, we could listen for Event.ENTER_FRAME events from a DisplayObject, but that seems like overkill for a once-off delayed function call.
Flex was intended to abstract away the frame-based nature of the Flash Player so you will not find much to help you with your problem. The best approach is to listen for ENTER_FRAME as you suggest. If that's overkill (and I'm not sure why you think it is), you could create a helper class which takes a DisplayObject and Function as arguments which will automatically add/remove the ENTER_FRAME event listener for you.
public class NextFrameCommand() {
private var functionToCall : Function;
public function NextFrameCommand(dObj: DisplayObject, func : Function) {
functionToCall = func;
}
public function start() : void {
dObj.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e : Event) : void {
e.target.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
functionToCall.call();
}
}
I haven't tested that code but hopefully you get the idea....
Playing with the solutions provided by Theo and brd6644 I came us with this. It allows multiple functions (with parameters) to be queued and executed in order at the next frame.
public class DelayedFunctionQueue
{
protected var queue:Array;
protected var dispatcher:Sprite;
public function DelayedFunctionQueue()
{
queue = new Array();
dispatcher = new Sprite();
}
public function add( func:Function, ... args ):void
{
var delegateFn:Function = function():void
{
func.apply( null, args );
}
queue.push( delegateFn );
if ( queue.length == 1 )
{
dispatcher.addEventListener( Event.ENTER_FRAME, onEF, false, 0, true );
}
}
protected function onEF( event:Event ):void
{
dispatcher.removeEventListener( Event.ENTER_FRAME, onEF, false );
queue = queue.reverse();
while ( queue.length > 0 )
{
var delegateFn:Function = queue.pop();
delegateFn();
}
}
}
Might be useful to someone.
One way would be to see how many frames per second your project is set for and let the setTimeout function delay for 1 frame time within that setting.
So if your project is set at 24 frames per second, you delay for 42 millisec in setTimeout.
var timerSprite:Sprite = new Sprite();
timerSprite.addEventListener(Event.ENTER_FRAME, oneFrameHandeler);
function oneFrameHandeler(e:Event):void
{
timerSprite.removeEventListener(Event.ENTER_FRAME, oneFrameHandeler);
timerSprite = null;
trace("one frame later!")
}
ENTER_FRAME is not overkill - something like this is short and easy
addEventListener(Event.ENTER_FRAME, function(e:Event):void
{
removeEventListener(Event.ENTER_FRAME, arguments["callee"]);
onceOffFunction();
}