You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@royale.apache.org by Harbs <ha...@gmail.com> on 2022/01/05 12:21:57 UTC
Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
So this is weird.
Function.prototype.apply is supposed to be able to take an array of arguments and apply them to another function. (different from call where you have to specify the arguments separately.)
It looks to me like a function which was bound to an object (such as one wrapped by Language.closure) has the array passed into the first argument as an array instead of being passed in as a list of arguments.
I have not seen this documented anywhere, but that’s what I’m seeing.
Hmm. Actually, not sure about this. goog.bind is doing some weird stuff. Maybe that’s causing what I’m seeing? Why are we using goog.bind and not just the following in Language.closure?
static public function closure(fn:Function, object:Object):Function {
return function() {
return fn.apply(object, arguments);
};
}
My understanding is that code will correctly bind the function call to this.
It’s simpler, doesn’t require goog.bind and doesn’t require passing in function names.
Am I missing something?
Harbs
> On Jan 5, 2022, at 9:37 AM, Harbs <ha...@gmail.com> wrote:
>
> This function is a cool one. It lets you animate *anything*. You just specify a target fps and run an arbitrary function and it’ll automatically be spaced to execute at the target speed:
>
> The following will increment a value once every 50ms (1000/20fps) and run exactly 30 times.
>
> var value:Number = 0;
> function increment(val:Number):void{
> value+=val;
> }
> var animated:Function = animateFunction(increment,20);
> var savedValue:Number;
> for(i=0;i<30;i++){
> animated(1);
> }
>
> Have fun! B-)
> Harbs
>
>> On Jan 5, 2022, at 9:31 AM, harbs@apache.org wrote:
>>
>> This is an automated email from the ASF dual-hosted git repository.
>>
>> harbs pushed a commit to branch develop
>> in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
>>
>>
>> The following commit(s) were added to refs/heads/develop by this push:
>> new dd1c6e4 Added animateFunction
>> dd1c6e4 is described below
>>
>> commit dd1c6e4cd7b082207a39efbf71de31adbb6de0d9
>> Author: Harbs <ha...@in-tools.com>
>> AuthorDate: Wed Jan 5 09:31:10 2022 +0200
>>
>> Added animateFunction
>> ---
>> .../projects/Core/src/main/royale/CoreClasses.as | 1 +
>> .../royale/utils/functional/animateFunction.as | 130 +++++++++++++++++++++
>> .../src/test/royale/flexUnitTests/CoreTester.as | 2 +-
>> .../test/royale/flexUnitTests/FunctionalTests.as | 34 ++++++
>> 4 files changed, 166 insertions(+), 1 deletion(-)
>>
>> diff --git a/frameworks/projects/Core/src/main/royale/CoreClasses.as b/frameworks/projects/Core/src/main/royale/CoreClasses.as
>> index 50c3c71..48189b5 100644
>> --- a/frameworks/projects/Core/src/main/royale/CoreClasses.as
>> +++ b/frameworks/projects/Core/src/main/royale/CoreClasses.as
>> @@ -382,6 +382,7 @@ internal class CoreClasses
>> import org.apache.royale.utils.functional.debounceLong; debounceLong;
>> import org.apache.royale.utils.functional.debounceShort; debounceShort;
>> import org.apache.royale.utils.functional.throttle; throttle;
>> + import org.apache.royale.utils.functional.animateFunction; animateFunction;
>>
>> import org.apache.royale.core.TextLineMetrics; TextLineMetrics;
>> import org.apache.royale.utils.ClassSelectorList; ClassSelectorList;
>> diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
>> new file mode 100644
>> index 0000000..32b9fbf
>> --- /dev/null
>> +++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
>> @@ -0,0 +1,130 @@
>> +////////////////////////////////////////////////////////////////////////////////
>> +//
>> +// Licensed to the Apache Software Foundation (ASF) under one or more
>> +// contributor license agreements. See the NOTICE file distributed with
>> +// this work for additional information regarding copyright ownership.
>> +// The ASF licenses this file to You under the Apache License, Version 2.0
>> +// (the "License"); you may not use this file except in compliance with
>> +// the License. You may obtain a copy of the License at
>> +//
>> +// http://www.apache.org/licenses/LICENSE-2.0
>> +//
>> +// Unless required by applicable law or agreed to in writing, software
>> +// distributed under the License is distributed on an "AS IS" BASIS,
>> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>> +// See the License for the specific language governing permissions and
>> +// limitations under the License.
>> +//
>> +////////////////////////////////////////////////////////////////////////////////
>> +package org.apache.royale.utils.functional
>> +{
>> + COMPILE::SWF{
>> + import flash.utils.setTimeout;
>> + import flash.utils.clearTimeout;
>> + }
>> + /**
>> + * Returns a debounced function to run after a delay.
>> + * The first invocation of the function will be run after its delay.
>> + * Any invocations between the first invocation and the delay will be ignored.
>> + *
>> + * @royalesuppressexport
>> + * @langversion 3.0
>> + * @productversion Royale 0.9.9
>> + *
>> + */
>> + public function animateFunction(method:Function, fps:Number):Function
>> + {
>> + COMPILE::SWF
>> + {
>> + var limit:Number = 1000/fps;
>> + var timeStamp:Number = 0;
>> + var timeoutRef:*;
>> + var invocations:Array = [];
>> + return function(...args):void
>> + {
>> + if(timeoutRef){
>> + clearTimeout(timeoutRef);
>> + timeoutRef = null;
>> + }
>> + invocations.push(args);
>> + var currentTime:Number = new Date().getTime();
>> + var timeDiff:Number = currentTime - timeStamp;
>> + if(timeDiff >= limit)
>> + {
>> + if(timeStamp == 0)
>> + timeStamp = currentTime;
>> + else
>> + timeStamp += limit;
>> + method.apply(null,invocations.shift());
>> + }
>> + if(invocations.length && timeoutRef == null)
>> + {
>> + // currentTime = new Date().getTime();
>> + timeDiff = currentTime - timeStamp + limit;
>> + var nextInterval:Number = Math.max(timeDiff,0);
>> + timeoutRef = setTimeout(callback, nextInterval);
>> + }
>> +
>> + function callback():void
>> + {
>> + timeoutRef = null;
>> +
>> + if(!invocations.length)
>> + return;
>> +
>> + var currentArgs:Array = invocations.shift();
>> + method.apply(null,currentArgs);
>> + timeStamp += limit;
>> + var timeDiff:Number = new Date().getTime() - timeStamp + limit;
>> + while(timeDiff < 0)
>> + {
>> + // catch up on the missing frames
>> + method.apply(null,invocations.shift());
>> + if(invocations.length == 0)
>> + {
>> + return;
>> + }
>> + timeDiff+=limit;
>> + }
>> + if(invocations.length)
>> + {
>> + timeoutRef = setTimeout(callback, timeDiff);
>> + }
>> + }
>> + }
>> +
>> + }
>> +
>> + COMPILE::JS
>> + {
>> + var limit:Number = 1000/fps;
>> + var lastTimeStamp:Number = 0;
>> + var timeoutRef:*;
>> + var invocations:Array = [];
>> + return function(...args):void
>> + {
>> + invocations.push(args);
>> + requestAnimationFrame(callback);
>> + function callback(timeStamp:Number):void
>> + {
>> + if(invocations.length == 0)
>> + return;
>> +
>> + // we can't rely on getting time stamps ourselves,
>> + // so hopefully this is not slower than our target rate...
>> + if ( (timeStamp - lastTimeStamp) >= limit)
>> + {
>> + if(lastTimeStamp == 0)
>> + lastTimeStamp = timeStamp;
>> + else
>> + lastTimeStamp += limit; // make sure we stick to the desired rate
>> +
>> + method.apply(null,invocations.shift());
>> + }
>> + if(invocations.length)
>> + requestAnimationFrame(callback);
>> + }
>> + }
>> + }
>> + }
>> +}
>> \ No newline at end of file
>> diff --git a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
>> index 7b15375..eeb4e76 100644
>> --- a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
>> +++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
>> @@ -45,7 +45,7 @@ package flexUnitTests
>> public var sanitizerTest:SanitizeTest;
>> public var eventsTest:EventsTest;
>> public var objectUtilTests:ObjectUtilsTest;
>> - public var functionelTests:FunctionalTests;
>> + public var functionalTests:FunctionalTests;
>>
>> }
>> }
>> diff --git a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
>> index 022c4da..75f320c 100644
>> --- a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
>> +++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
>> @@ -25,6 +25,8 @@ package flexUnitTests
>> import org.apache.royale.test.asserts.*;
>> import org.apache.royale.test.async.*;
>> import org.apache.royale.utils.functional.*;
>> + import org.apache.royale.utils.functional.animateFunction;
>> + import org.apache.royale.test.asserts.assertTrue;
>>
>> public class FunctionalTests
>> {
>> @@ -192,6 +194,38 @@ package flexUnitTests
>> assertEquals(value,7,"value should be 7");
>> }, 300);
>> }
>> + [Test(async,timeout="300")]
>> + public function testAnimate():void
>> + {
>> + var foo:Foo = new Foo();
>> + var animateThis:Function = animateFunction(foo.increment,20);
>> + for(var i:int=0;i<30;i++){
>> + animateThis(1);
>> + }
>> + var savedThisValue:Number;
>> + setTimeout(function():void{
>> + savedThisValue = foo.value;
>> + },50);
>> +
>> + var value:Number = 0;
>> + function increment(val:Number):void{
>> + value+=val;
>> + }
>> + var animated:Function = animateFunction(increment,20);
>> + var savedValue:Number;
>> + for(i=0;i<30;i++){
>> + animated(1);
>> + }
>> +
>> + setTimeout(function():void{
>> + savedValue = value;
>> + },50);
>> + Async.delayCall(this, function():void
>> + {
>> + assertTrue(savedThisValue<3,"foo value should be 2");
>> + assertTrue(savedValue<3,"value should be 2");
>> + }, 300);
>> + }
>>
>>
>> }
>
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Alex Harui <ah...@adobe.com.INVALID>.
We might be able to stop using goog.bind in favor of some other pattern, but as you learned, the compiler must be able to generate the same closure instance for any class instance method when passed in as a parameter or stored in a variable (and maybe some other scenarios).
It is probably possible to come up with other schemes, but the scheme should probably handle scenarios in legacy code where addEventListener and removeEventListener get called more than once on the same AS closure.
HTH,
-Alex
On 1/5/22, 9:27 AM, "Harbs" <ha...@gmail.com> wrote:
Oh. I think I understand.
removeEventListener needs to reference the same function as addEventListener which needs to be unique to an instance but match on subsequent checks.
That’s what I was missing.
Never mind, and sorry for the noise… ;-)
Although, I wonder if there’s some way we can optimize “one time” event listeners…
Harbs
> On Jan 5, 2022, at 7:15 PM, Harbs <ha...@gmail.com> wrote:
>
> FWIW, I think the reason the goog version is so complicated is because it supports partially applying arguments when you first use goog.bind (which is not something we’re doing).
>
> But I definitely would like to hear why it’s important for two bound methods to be equivalent. I’m probably missing something, but I can’t see where it would matter.
>
> Caching the closure seems like it just adds bulk to instances and make an app use more memory.
>
> Probably the most common use case of Language.closure is for event listeners. If the event listener is transient, it can be cleaned up by garbage collection. If it’s cached to the instance, it’ll live for the life of the instance. Seems like a down-side to me.
>
> Harbs
>
>> On Jan 5, 2022, at 6:37 PM, Harbs <harbs.lists@gmail.com <ma...@gmail.com>> wrote:
>>
>> On normal modern systems it’s roughly quivalent to this:
>>
>> function(fn, selfObj, var_args){
>> return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
>> }.apply(null,arguments);
>>
>>
>> Here’s the code:
>>
>> goog.bindNative_ = function(fn, selfObj, var_args) {
>> return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
>> };
>>
>> goog.bind = function(fn, selfObj, var_args) {
>> // TODO(nicksantos): narrow the type signature.
>> if (Function.prototype.bind &&
>> // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
>> // extension environment. This means that for Chrome extensions, they get
>> // the implementation of Function.prototype.bind that calls goog.bind
>> // instead of the native one. Even worse, we don't want to introduce a
>> // circular dependency between goog.bind and Function.prototype.bind, so
>> // we have to hack this to make sure it works correctly.
>> Function.prototype.bind.toString().indexOf('native code') != -1) {
>> goog.bind = goog.bindNative_;
>> } else {
>> goog.bind = goog.bindJs_;
>> }
>> return goog.bind.apply(null, arguments);
>> };
>>
>> Not sure why you’d want to use two applies and one bind instead of a single apply in:
>>
>> return function() {
>> return fn.apply(object, arguments);
>> };
>>
>>> Did you test this with your chsnges:
>>>
>>> var f1:Function = myInst.method;
>>> var f2:Function = myInst.method;
>>>
>>> trace(f1 == f2)
>>
>>
>> No I didn’t. You’re right that will likely fail. But why is that important?
>>
>> Thanks,
>> Harbs
>>
>>> On Jan 5, 2022, at 5:00 PM, Greg Dove <greg.dove@gmail.com <ma...@gmail.com>> wrote:
>>>
>>> I would assume that goog.bind would be optimized by gcc if it can be
>>> (perhaps it is inlined if it uses es5 function.protoype.bind internally- I
>>> did not look inside it)
>>>
>>> The function naming/caching in Language.closure is important, including
>>> private naming conventions when they are needed for closures.
>>>
>>> Did you test this with your chsnges:
>>>
>>> var f1:Function = myInst.method;
>>> var f2:Function = myInst.method;
>>>
>>> trace(f1 == f2)
>>
>
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Harbs <ha...@gmail.com>.
Oh. I think I understand.
removeEventListener needs to reference the same function as addEventListener which needs to be unique to an instance but match on subsequent checks.
That’s what I was missing.
Never mind, and sorry for the noise… ;-)
Although, I wonder if there’s some way we can optimize “one time” event listeners…
Harbs
> On Jan 5, 2022, at 7:15 PM, Harbs <ha...@gmail.com> wrote:
>
> FWIW, I think the reason the goog version is so complicated is because it supports partially applying arguments when you first use goog.bind (which is not something we’re doing).
>
> But I definitely would like to hear why it’s important for two bound methods to be equivalent. I’m probably missing something, but I can’t see where it would matter.
>
> Caching the closure seems like it just adds bulk to instances and make an app use more memory.
>
> Probably the most common use case of Language.closure is for event listeners. If the event listener is transient, it can be cleaned up by garbage collection. If it’s cached to the instance, it’ll live for the life of the instance. Seems like a down-side to me.
>
> Harbs
>
>> On Jan 5, 2022, at 6:37 PM, Harbs <harbs.lists@gmail.com <ma...@gmail.com>> wrote:
>>
>> On normal modern systems it’s roughly quivalent to this:
>>
>> function(fn, selfObj, var_args){
>> return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
>> }.apply(null,arguments);
>>
>>
>> Here’s the code:
>>
>> goog.bindNative_ = function(fn, selfObj, var_args) {
>> return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
>> };
>>
>> goog.bind = function(fn, selfObj, var_args) {
>> // TODO(nicksantos): narrow the type signature.
>> if (Function.prototype.bind &&
>> // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
>> // extension environment. This means that for Chrome extensions, they get
>> // the implementation of Function.prototype.bind that calls goog.bind
>> // instead of the native one. Even worse, we don't want to introduce a
>> // circular dependency between goog.bind and Function.prototype.bind, so
>> // we have to hack this to make sure it works correctly.
>> Function.prototype.bind.toString().indexOf('native code') != -1) {
>> goog.bind = goog.bindNative_;
>> } else {
>> goog.bind = goog.bindJs_;
>> }
>> return goog.bind.apply(null, arguments);
>> };
>>
>> Not sure why you’d want to use two applies and one bind instead of a single apply in:
>>
>> return function() {
>> return fn.apply(object, arguments);
>> };
>>
>>> Did you test this with your chsnges:
>>>
>>> var f1:Function = myInst.method;
>>> var f2:Function = myInst.method;
>>>
>>> trace(f1 == f2)
>>
>>
>> No I didn’t. You’re right that will likely fail. But why is that important?
>>
>> Thanks,
>> Harbs
>>
>>> On Jan 5, 2022, at 5:00 PM, Greg Dove <greg.dove@gmail.com <ma...@gmail.com>> wrote:
>>>
>>> I would assume that goog.bind would be optimized by gcc if it can be
>>> (perhaps it is inlined if it uses es5 function.protoype.bind internally- I
>>> did not look inside it)
>>>
>>> The function naming/caching in Language.closure is important, including
>>> private naming conventions when they are needed for closures.
>>>
>>> Did you test this with your chsnges:
>>>
>>> var f1:Function = myInst.method;
>>> var f2:Function = myInst.method;
>>>
>>> trace(f1 == f2)
>>
>
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Josh Tynjala <jo...@bowlerhat.dev>.
In SWF, I recall that all methods of an instance's class are automatically
bound to the instance. This should mean that the equality check passes. I
assume that we're trying to keep this same behavior when transpiling to JS.
--
Josh Tynjala
Bowler Hat LLC <https://bowlerhat.dev>
On Wed, Jan 5, 2022 at 9:16 AM Harbs <ha...@gmail.com> wrote:
> FWIW, I think the reason the goog version is so complicated is because it
> supports partially applying arguments when you first use goog.bind (which
> is not something we’re doing).
>
> But I definitely would like to hear why it’s important for two bound
> methods to be equivalent. I’m probably missing something, but I can’t see
> where it would matter.
>
> Caching the closure seems like it just adds bulk to instances and make an
> app use more memory.
>
> Probably the most common use case of Language.closure is for event
> listeners. If the event listener is transient, it can be cleaned up by
> garbage collection. If it’s cached to the instance, it’ll live for the life
> of the instance. Seems like a down-side to me.
>
> Harbs
>
> > On Jan 5, 2022, at 6:37 PM, Harbs <ha...@gmail.com> wrote:
> >
> > On normal modern systems it’s roughly quivalent to this:
> >
> > function(fn, selfObj, var_args){
> > return /** @type {!Function} */ (fn.call.apply(fn.bind,
> arguments));
> > }.apply(null,arguments);
> >
> >
> > Here’s the code:
> >
> > goog.bindNative_ = function(fn, selfObj, var_args) {
> > return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
> > };
> >
> > goog.bind = function(fn, selfObj, var_args) {
> > // TODO(nicksantos): narrow the type signature.
> > if (Function.prototype.bind &&
> > // NOTE(nicksantos): Somebody pulled base.js into the default
> Chrome
> > // extension environment. This means that for Chrome extensions,
> they get
> > // the implementation of Function.prototype.bind that calls
> goog.bind
> > // instead of the native one. Even worse, we don't want to
> introduce a
> > // circular dependency between goog.bind and
> Function.prototype.bind, so
> > // we have to hack this to make sure it works correctly.
> > Function.prototype.bind.toString().indexOf('native code') != -1) {
> > goog.bind = goog.bindNative_;
> > } else {
> > goog.bind = goog.bindJs_;
> > }
> > return goog.bind.apply(null, arguments);
> > };
> >
> > Not sure why you’d want to use two applies and one bind instead of a
> single apply in:
> >
> > return function() {
> > return fn.apply(object, arguments);
> > };
> >
> >> Did you test this with your chsnges:
> >>
> >> var f1:Function = myInst.method;
> >> var f2:Function = myInst.method;
> >>
> >> trace(f1 == f2)
> >
> >
> > No I didn’t. You’re right that will likely fail. But why is that
> important?
> >
> > Thanks,
> > Harbs
> >
> >> On Jan 5, 2022, at 5:00 PM, Greg Dove <greg.dove@gmail.com <mailto:
> greg.dove@gmail.com>> wrote:
> >>
> >> I would assume that goog.bind would be optimized by gcc if it can be
> >> (perhaps it is inlined if it uses es5 function.protoype.bind
> internally- I
> >> did not look inside it)
> >>
> >> The function naming/caching in Language.closure is important, including
> >> private naming conventions when they are needed for closures.
> >>
> >> Did you test this with your chsnges:
> >>
> >> var f1:Function = myInst.method;
> >> var f2:Function = myInst.method;
> >>
> >> trace(f1 == f2)
> >
>
>
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Harbs <ha...@gmail.com>.
FWIW, I think the reason the goog version is so complicated is because it supports partially applying arguments when you first use goog.bind (which is not something we’re doing).
But I definitely would like to hear why it’s important for two bound methods to be equivalent. I’m probably missing something, but I can’t see where it would matter.
Caching the closure seems like it just adds bulk to instances and make an app use more memory.
Probably the most common use case of Language.closure is for event listeners. If the event listener is transient, it can be cleaned up by garbage collection. If it’s cached to the instance, it’ll live for the life of the instance. Seems like a down-side to me.
Harbs
> On Jan 5, 2022, at 6:37 PM, Harbs <ha...@gmail.com> wrote:
>
> On normal modern systems it’s roughly quivalent to this:
>
> function(fn, selfObj, var_args){
> return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
> }.apply(null,arguments);
>
>
> Here’s the code:
>
> goog.bindNative_ = function(fn, selfObj, var_args) {
> return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
> };
>
> goog.bind = function(fn, selfObj, var_args) {
> // TODO(nicksantos): narrow the type signature.
> if (Function.prototype.bind &&
> // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
> // extension environment. This means that for Chrome extensions, they get
> // the implementation of Function.prototype.bind that calls goog.bind
> // instead of the native one. Even worse, we don't want to introduce a
> // circular dependency between goog.bind and Function.prototype.bind, so
> // we have to hack this to make sure it works correctly.
> Function.prototype.bind.toString().indexOf('native code') != -1) {
> goog.bind = goog.bindNative_;
> } else {
> goog.bind = goog.bindJs_;
> }
> return goog.bind.apply(null, arguments);
> };
>
> Not sure why you’d want to use two applies and one bind instead of a single apply in:
>
> return function() {
> return fn.apply(object, arguments);
> };
>
>> Did you test this with your chsnges:
>>
>> var f1:Function = myInst.method;
>> var f2:Function = myInst.method;
>>
>> trace(f1 == f2)
>
>
> No I didn’t. You’re right that will likely fail. But why is that important?
>
> Thanks,
> Harbs
>
>> On Jan 5, 2022, at 5:00 PM, Greg Dove <greg.dove@gmail.com <ma...@gmail.com>> wrote:
>>
>> I would assume that goog.bind would be optimized by gcc if it can be
>> (perhaps it is inlined if it uses es5 function.protoype.bind internally- I
>> did not look inside it)
>>
>> The function naming/caching in Language.closure is important, including
>> private naming conventions when they are needed for closures.
>>
>> Did you test this with your chsnges:
>>
>> var f1:Function = myInst.method;
>> var f2:Function = myInst.method;
>>
>> trace(f1 == f2)
>
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Harbs <ha...@gmail.com>.
On normal modern systems it’s roughly quivalent to this:
function(fn, selfObj, var_args){
return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
}.apply(null,arguments);
Here’s the code:
goog.bindNative_ = function(fn, selfObj, var_args) {
return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
};
goog.bind = function(fn, selfObj, var_args) {
// TODO(nicksantos): narrow the type signature.
if (Function.prototype.bind &&
// NOTE(nicksantos): Somebody pulled base.js into the default Chrome
// extension environment. This means that for Chrome extensions, they get
// the implementation of Function.prototype.bind that calls goog.bind
// instead of the native one. Even worse, we don't want to introduce a
// circular dependency between goog.bind and Function.prototype.bind, so
// we have to hack this to make sure it works correctly.
Function.prototype.bind.toString().indexOf('native code') != -1) {
goog.bind = goog.bindNative_;
} else {
goog.bind = goog.bindJs_;
}
return goog.bind.apply(null, arguments);
};
Not sure why you’d want to use two applies and one bind instead of a single apply in:
return function() {
return fn.apply(object, arguments);
};
> Did you test this with your chsnges:
>
> var f1:Function = myInst.method;
> var f2:Function = myInst.method;
>
> trace(f1 == f2)
No I didn’t. You’re right that will likely fail. But why is that important?
Thanks,
Harbs
> On Jan 5, 2022, at 5:00 PM, Greg Dove <gr...@gmail.com> wrote:
>
> I would assume that goog.bind would be optimized by gcc if it can be
> (perhaps it is inlined if it uses es5 function.protoype.bind internally- I
> did not look inside it)
>
> The function naming/caching in Language.closure is important, including
> private naming conventions when they are needed for closures.
>
> Did you test this with your chsnges:
>
> var f1:Function = myInst.method;
> var f2:Function = myInst.method;
>
> trace(f1 == f2)
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Greg Dove <gr...@gmail.com>.
I would assume that goog.bind would be optimized by gcc if it can be
(perhaps it is inlined if it uses es5 function.protoype.bind internally- I
did not look inside it)
The function naming/caching in Language.closure is important, including
private naming conventions when they are needed for closures.
Did you test this with your chsnges:
var f1:Function = myInst.method;
var f2:Function = myInst.method;
trace(f1 == f2)
On Thu, 6 Jan 2022, 1:37 am Harbs, <ha...@gmail.com> wrote:
> My problem with apply was user error because I was using spread arguments
> which was actually creating an arrays of arrays.
>
> But my question about Language.closure stands.
>
> In my tests it looks like that works.
>
> > On Jan 5, 2022, at 2:21 PM, Harbs <ha...@gmail.com> wrote:
> >
> > So this is weird.
> >
> > Function.prototype.apply is supposed to be able to take an array of
> arguments and apply them to another function. (different from call where
> you have to specify the arguments separately.)
> >
> > It looks to me like a function which was bound to an object (such as one
> wrapped by Language.closure) has the array passed into the first argument
> as an array instead of being passed in as a list of arguments.
> >
> > I have not seen this documented anywhere, but that’s what I’m seeing.
> >
> > Hmm. Actually, not sure about this. goog.bind is doing some weird stuff.
> Maybe that’s causing what I’m seeing? Why are we using goog.bind and not
> just the following in Language.closure?
> >
> > static public function closure(fn:Function, object:Object):Function {
> > return function() {
> > return fn.apply(object, arguments);
> > };
> > }
> >
> > My understanding is that code will correctly bind the function call to
> this.
> >
> > It’s simpler, doesn’t require goog.bind and doesn’t require passing in
> function names.
> >
> > Am I missing something?
> >
> > Harbs
> >
> >> On Jan 5, 2022, at 9:37 AM, Harbs <ha...@gmail.com> wrote:
> >>
> >> This function is a cool one. It lets you animate *anything*. You just
> specify a target fps and run an arbitrary function and it’ll automatically
> be spaced to execute at the target speed:
> >>
> >> The following will increment a value once every 50ms (1000/20fps) and
> run exactly 30 times.
> >>
> >> var value:Number = 0;
> >> function increment(val:Number):void{
> >> value+=val;
> >> }
> >> var animated:Function = animateFunction(increment,20);
> >> var savedValue:Number;
> >> for(i=0;i<30;i++){
> >> animated(1);
> >> }
> >>
> >> Have fun! B-)
> >> Harbs
> >>
> >>> On Jan 5, 2022, at 9:31 AM, harbs@apache.org wrote:
> >>>
> >>> This is an automated email from the ASF dual-hosted git repository.
> >>>
> >>> harbs pushed a commit to branch develop
> >>> in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
> >>>
> >>>
> >>> The following commit(s) were added to refs/heads/develop by this push:
> >>> new dd1c6e4 Added animateFunction
> >>> dd1c6e4 is described below
> >>>
> >>> commit dd1c6e4cd7b082207a39efbf71de31adbb6de0d9
> >>> Author: Harbs <ha...@in-tools.com>
> >>> AuthorDate: Wed Jan 5 09:31:10 2022 +0200
> >>>
> >>> Added animateFunction
> >>> ---
> >>> .../projects/Core/src/main/royale/CoreClasses.as | 1 +
> >>> .../royale/utils/functional/animateFunction.as | 130
> +++++++++++++++++++++
> >>> .../src/test/royale/flexUnitTests/CoreTester.as | 2 +-
> >>> .../test/royale/flexUnitTests/FunctionalTests.as | 34 ++++++
> >>> 4 files changed, 166 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/frameworks/projects/Core/src/main/royale/CoreClasses.as
> b/frameworks/projects/Core/src/main/royale/CoreClasses.as
> >>> index 50c3c71..48189b5 100644
> >>> --- a/frameworks/projects/Core/src/main/royale/CoreClasses.as
> >>> +++ b/frameworks/projects/Core/src/main/royale/CoreClasses.as
> >>> @@ -382,6 +382,7 @@ internal class CoreClasses
> >>> import org.apache.royale.utils.functional.debounceLong;
> debounceLong;
> >>> import org.apache.royale.utils.functional.debounceShort;
> debounceShort;
> >>> import org.apache.royale.utils.functional.throttle; throttle;
> >>> + import org.apache.royale.utils.functional.animateFunction;
> animateFunction;
> >>>
> >>> import org.apache.royale.core.TextLineMetrics; TextLineMetrics;
> >>> import org.apache.royale.utils.ClassSelectorList;
> ClassSelectorList;
> >>> diff --git
> a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
> b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
> >>> new file mode 100644
> >>> index 0000000..32b9fbf
> >>> --- /dev/null
> >>> +++
> b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
> >>> @@ -0,0 +1,130 @@
> >>>
> +////////////////////////////////////////////////////////////////////////////////
> >>> +//
> >>> +// Licensed to the Apache Software Foundation (ASF) under one or more
> >>> +// contributor license agreements. See the NOTICE file distributed
> with
> >>> +// this work for additional information regarding copyright
> ownership.
> >>> +// The ASF licenses this file to You under the Apache License,
> Version 2.0
> >>> +// (the "License"); you may not use this file except in compliance
> with
> >>> +// the License. You may obtain a copy of the License at
> >>> +//
> >>> +// http://www.apache.org/licenses/LICENSE-2.0
> >>> +//
> >>> +// Unless required by applicable law or agreed to in writing,
> software
> >>> +// distributed under the License is distributed on an "AS IS" BASIS,
> >>> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> >>> +// See the License for the specific language governing permissions
> and
> >>> +// limitations under the License.
> >>> +//
> >>>
> +////////////////////////////////////////////////////////////////////////////////
> >>> +package org.apache.royale.utils.functional
> >>> +{
> >>> + COMPILE::SWF{
> >>> + import flash.utils.setTimeout;
> >>> + import flash.utils.clearTimeout;
> >>> + }
> >>> + /**
> >>> + * Returns a debounced function to run after a delay.
> >>> + * The first invocation of the function will be run after its
> delay.
> >>> + * Any invocations between the first invocation and the delay will
> be ignored.
> >>> + *
> >>> + * @royalesuppressexport
> >>> + * @langversion 3.0
> >>> + * @productversion Royale 0.9.9
> >>> + *
> >>> + */
> >>> + public function animateFunction(method:Function,
> fps:Number):Function
> >>> + {
> >>> + COMPILE::SWF
> >>> + {
> >>> + var limit:Number = 1000/fps;
> >>> + var timeStamp:Number = 0;
> >>> + var timeoutRef:*;
> >>> + var invocations:Array = [];
> >>> + return function(...args):void
> >>> + {
> >>> + if(timeoutRef){
> >>> + clearTimeout(timeoutRef);
> >>> + timeoutRef = null;
> >>> + }
> >>> + invocations.push(args);
> >>> + var currentTime:Number = new
> Date().getTime();
> >>> + var timeDiff:Number = currentTime -
> timeStamp;
> >>> + if(timeDiff >= limit)
> >>> + {
> >>> + if(timeStamp == 0)
> >>> + timeStamp = currentTime;
> >>> + else
> >>> + timeStamp += limit;
> >>> +
> method.apply(null,invocations.shift());
> >>> + }
> >>> + if(invocations.length && timeoutRef ==
> null)
> >>> + {
> >>> + // currentTime = new
> Date().getTime();
> >>> + timeDiff = currentTime - timeStamp
> + limit;
> >>> + var nextInterval:Number =
> Math.max(timeDiff,0);
> >>> + timeoutRef = setTimeout(callback,
> nextInterval);
> >>> + }
> >>> +
> >>> + function callback():void
> >>> + {
> >>> + timeoutRef = null;
> >>> +
> >>> + if(!invocations.length)
> >>> + return;
> >>> +
> >>> + var currentArgs:Array =
> invocations.shift();
> >>> + method.apply(null,currentArgs);
> >>> + timeStamp += limit;
> >>> + var timeDiff:Number = new
> Date().getTime() - timeStamp + limit;
> >>> + while(timeDiff < 0)
> >>> + {
> >>> + // catch up on the missing
> frames
> >>> +
> method.apply(null,invocations.shift());
> >>> + if(invocations.length == 0)
> >>> + {
> >>> + return;
> >>> + }
> >>> + timeDiff+=limit;
> >>> + }
> >>> + if(invocations.length)
> >>> + {
> >>> + timeoutRef =
> setTimeout(callback, timeDiff);
> >>> + }
> >>> + }
> >>> + }
> >>> +
> >>> + }
> >>> +
> >>> + COMPILE::JS
> >>> + {
> >>> + var limit:Number = 1000/fps;
> >>> + var lastTimeStamp:Number = 0;
> >>> + var timeoutRef:*;
> >>> + var invocations:Array = [];
> >>> + return function(...args):void
> >>> + {
> >>> + invocations.push(args);
> >>> + requestAnimationFrame(callback);
> >>> + function callback(timeStamp:Number):void
> >>> + {
> >>> + if(invocations.length == 0)
> >>> + return;
> >>> +
> >>> + // we can't rely on getting time
> stamps ourselves,
> >>> + // so hopefully this is not slower
> than our target rate...
> >>> + if ( (timeStamp - lastTimeStamp)
> >= limit)
> >>> + {
> >>> + if(lastTimeStamp == 0)
> >>> + lastTimeStamp =
> timeStamp;
> >>> + else
> >>> + lastTimeStamp +=
> limit; // make sure we stick to the desired rate
> >>> +
> >>> +
> method.apply(null,invocations.shift());
> >>> + }
> >>> + if(invocations.length)
> >>> +
> requestAnimationFrame(callback);
> >>> + }
> >>> + }
> >>> + }
> >>> + }
> >>> +}
> >>> \ No newline at end of file
> >>> diff --git
> a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
> b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
> >>> index 7b15375..eeb4e76 100644
> >>> ---
> a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
> >>> +++
> b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
> >>> @@ -45,7 +45,7 @@ package flexUnitTests
> >>> public var sanitizerTest:SanitizeTest;
> >>> public var eventsTest:EventsTest;
> >>> public var objectUtilTests:ObjectUtilsTest;
> >>> - public var functionelTests:FunctionalTests;
> >>> + public var functionalTests:FunctionalTests;
> >>>
> >>> }
> >>> }
> >>> diff --git
> a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
> b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
> >>> index 022c4da..75f320c 100644
> >>> ---
> a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
> >>> +++
> b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
> >>> @@ -25,6 +25,8 @@ package flexUnitTests
> >>> import org.apache.royale.test.asserts.*;
> >>> import org.apache.royale.test.async.*;
> >>> import org.apache.royale.utils.functional.*;
> >>> + import org.apache.royale.utils.functional.animateFunction;
> >>> + import org.apache.royale.test.asserts.assertTrue;
> >>>
> >>> public class FunctionalTests
> >>> {
> >>> @@ -192,6 +194,38 @@ package flexUnitTests
> >>> assertEquals(value,7,"value should be 7");
> >>> }, 300);
> >>> }
> >>> + [Test(async,timeout="300")]
> >>> + public function testAnimate():void
> >>> + {
> >>> + var foo:Foo = new Foo();
> >>> + var animateThis:Function =
> animateFunction(foo.increment,20);
> >>> + for(var i:int=0;i<30;i++){
> >>> + animateThis(1);
> >>> + }
> >>> + var savedThisValue:Number;
> >>> + setTimeout(function():void{
> >>> + savedThisValue = foo.value;
> >>> + },50);
> >>> +
> >>> + var value:Number = 0;
> >>> + function increment(val:Number):void{
> >>> + value+=val;
> >>> + }
> >>> + var animated:Function = animateFunction(increment,20);
> >>> + var savedValue:Number;
> >>> + for(i=0;i<30;i++){
> >>> + animated(1);
> >>> + }
> >>> +
> >>> + setTimeout(function():void{
> >>> + savedValue = value;
> >>> + },50);
> >>> + Async.delayCall(this, function():void
> >>> + {
> >>> + assertTrue(savedThisValue<3,"foo value should be 2");
> >>> + assertTrue(savedValue<3,"value should be 2");
> >>> + }, 300);
> >>> + }
> >>>
> >>>
> >>> }
> >>
> >
>
>
Re: Language.closure (was Re: [royale-asjs] branch develop updated: Added animateFunction)
Posted by Harbs <ha...@gmail.com>.
My problem with apply was user error because I was using spread arguments which was actually creating an arrays of arrays.
But my question about Language.closure stands.
In my tests it looks like that works.
> On Jan 5, 2022, at 2:21 PM, Harbs <ha...@gmail.com> wrote:
>
> So this is weird.
>
> Function.prototype.apply is supposed to be able to take an array of arguments and apply them to another function. (different from call where you have to specify the arguments separately.)
>
> It looks to me like a function which was bound to an object (such as one wrapped by Language.closure) has the array passed into the first argument as an array instead of being passed in as a list of arguments.
>
> I have not seen this documented anywhere, but that’s what I’m seeing.
>
> Hmm. Actually, not sure about this. goog.bind is doing some weird stuff. Maybe that’s causing what I’m seeing? Why are we using goog.bind and not just the following in Language.closure?
>
> static public function closure(fn:Function, object:Object):Function {
> return function() {
> return fn.apply(object, arguments);
> };
> }
>
> My understanding is that code will correctly bind the function call to this.
>
> It’s simpler, doesn’t require goog.bind and doesn’t require passing in function names.
>
> Am I missing something?
>
> Harbs
>
>> On Jan 5, 2022, at 9:37 AM, Harbs <ha...@gmail.com> wrote:
>>
>> This function is a cool one. It lets you animate *anything*. You just specify a target fps and run an arbitrary function and it’ll automatically be spaced to execute at the target speed:
>>
>> The following will increment a value once every 50ms (1000/20fps) and run exactly 30 times.
>>
>> var value:Number = 0;
>> function increment(val:Number):void{
>> value+=val;
>> }
>> var animated:Function = animateFunction(increment,20);
>> var savedValue:Number;
>> for(i=0;i<30;i++){
>> animated(1);
>> }
>>
>> Have fun! B-)
>> Harbs
>>
>>> On Jan 5, 2022, at 9:31 AM, harbs@apache.org wrote:
>>>
>>> This is an automated email from the ASF dual-hosted git repository.
>>>
>>> harbs pushed a commit to branch develop
>>> in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
>>>
>>>
>>> The following commit(s) were added to refs/heads/develop by this push:
>>> new dd1c6e4 Added animateFunction
>>> dd1c6e4 is described below
>>>
>>> commit dd1c6e4cd7b082207a39efbf71de31adbb6de0d9
>>> Author: Harbs <ha...@in-tools.com>
>>> AuthorDate: Wed Jan 5 09:31:10 2022 +0200
>>>
>>> Added animateFunction
>>> ---
>>> .../projects/Core/src/main/royale/CoreClasses.as | 1 +
>>> .../royale/utils/functional/animateFunction.as | 130 +++++++++++++++++++++
>>> .../src/test/royale/flexUnitTests/CoreTester.as | 2 +-
>>> .../test/royale/flexUnitTests/FunctionalTests.as | 34 ++++++
>>> 4 files changed, 166 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/frameworks/projects/Core/src/main/royale/CoreClasses.as b/frameworks/projects/Core/src/main/royale/CoreClasses.as
>>> index 50c3c71..48189b5 100644
>>> --- a/frameworks/projects/Core/src/main/royale/CoreClasses.as
>>> +++ b/frameworks/projects/Core/src/main/royale/CoreClasses.as
>>> @@ -382,6 +382,7 @@ internal class CoreClasses
>>> import org.apache.royale.utils.functional.debounceLong; debounceLong;
>>> import org.apache.royale.utils.functional.debounceShort; debounceShort;
>>> import org.apache.royale.utils.functional.throttle; throttle;
>>> + import org.apache.royale.utils.functional.animateFunction; animateFunction;
>>>
>>> import org.apache.royale.core.TextLineMetrics; TextLineMetrics;
>>> import org.apache.royale.utils.ClassSelectorList; ClassSelectorList;
>>> diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
>>> new file mode 100644
>>> index 0000000..32b9fbf
>>> --- /dev/null
>>> +++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/functional/animateFunction.as
>>> @@ -0,0 +1,130 @@
>>> +////////////////////////////////////////////////////////////////////////////////
>>> +//
>>> +// Licensed to the Apache Software Foundation (ASF) under one or more
>>> +// contributor license agreements. See the NOTICE file distributed with
>>> +// this work for additional information regarding copyright ownership.
>>> +// The ASF licenses this file to You under the Apache License, Version 2.0
>>> +// (the "License"); you may not use this file except in compliance with
>>> +// the License. You may obtain a copy of the License at
>>> +//
>>> +// http://www.apache.org/licenses/LICENSE-2.0
>>> +//
>>> +// Unless required by applicable law or agreed to in writing, software
>>> +// distributed under the License is distributed on an "AS IS" BASIS,
>>> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>>> +// See the License for the specific language governing permissions and
>>> +// limitations under the License.
>>> +//
>>> +////////////////////////////////////////////////////////////////////////////////
>>> +package org.apache.royale.utils.functional
>>> +{
>>> + COMPILE::SWF{
>>> + import flash.utils.setTimeout;
>>> + import flash.utils.clearTimeout;
>>> + }
>>> + /**
>>> + * Returns a debounced function to run after a delay.
>>> + * The first invocation of the function will be run after its delay.
>>> + * Any invocations between the first invocation and the delay will be ignored.
>>> + *
>>> + * @royalesuppressexport
>>> + * @langversion 3.0
>>> + * @productversion Royale 0.9.9
>>> + *
>>> + */
>>> + public function animateFunction(method:Function, fps:Number):Function
>>> + {
>>> + COMPILE::SWF
>>> + {
>>> + var limit:Number = 1000/fps;
>>> + var timeStamp:Number = 0;
>>> + var timeoutRef:*;
>>> + var invocations:Array = [];
>>> + return function(...args):void
>>> + {
>>> + if(timeoutRef){
>>> + clearTimeout(timeoutRef);
>>> + timeoutRef = null;
>>> + }
>>> + invocations.push(args);
>>> + var currentTime:Number = new Date().getTime();
>>> + var timeDiff:Number = currentTime - timeStamp;
>>> + if(timeDiff >= limit)
>>> + {
>>> + if(timeStamp == 0)
>>> + timeStamp = currentTime;
>>> + else
>>> + timeStamp += limit;
>>> + method.apply(null,invocations.shift());
>>> + }
>>> + if(invocations.length && timeoutRef == null)
>>> + {
>>> + // currentTime = new Date().getTime();
>>> + timeDiff = currentTime - timeStamp + limit;
>>> + var nextInterval:Number = Math.max(timeDiff,0);
>>> + timeoutRef = setTimeout(callback, nextInterval);
>>> + }
>>> +
>>> + function callback():void
>>> + {
>>> + timeoutRef = null;
>>> +
>>> + if(!invocations.length)
>>> + return;
>>> +
>>> + var currentArgs:Array = invocations.shift();
>>> + method.apply(null,currentArgs);
>>> + timeStamp += limit;
>>> + var timeDiff:Number = new Date().getTime() - timeStamp + limit;
>>> + while(timeDiff < 0)
>>> + {
>>> + // catch up on the missing frames
>>> + method.apply(null,invocations.shift());
>>> + if(invocations.length == 0)
>>> + {
>>> + return;
>>> + }
>>> + timeDiff+=limit;
>>> + }
>>> + if(invocations.length)
>>> + {
>>> + timeoutRef = setTimeout(callback, timeDiff);
>>> + }
>>> + }
>>> + }
>>> +
>>> + }
>>> +
>>> + COMPILE::JS
>>> + {
>>> + var limit:Number = 1000/fps;
>>> + var lastTimeStamp:Number = 0;
>>> + var timeoutRef:*;
>>> + var invocations:Array = [];
>>> + return function(...args):void
>>> + {
>>> + invocations.push(args);
>>> + requestAnimationFrame(callback);
>>> + function callback(timeStamp:Number):void
>>> + {
>>> + if(invocations.length == 0)
>>> + return;
>>> +
>>> + // we can't rely on getting time stamps ourselves,
>>> + // so hopefully this is not slower than our target rate...
>>> + if ( (timeStamp - lastTimeStamp) >= limit)
>>> + {
>>> + if(lastTimeStamp == 0)
>>> + lastTimeStamp = timeStamp;
>>> + else
>>> + lastTimeStamp += limit; // make sure we stick to the desired rate
>>> +
>>> + method.apply(null,invocations.shift());
>>> + }
>>> + if(invocations.length)
>>> + requestAnimationFrame(callback);
>>> + }
>>> + }
>>> + }
>>> + }
>>> +}
>>> \ No newline at end of file
>>> diff --git a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
>>> index 7b15375..eeb4e76 100644
>>> --- a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
>>> +++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
>>> @@ -45,7 +45,7 @@ package flexUnitTests
>>> public var sanitizerTest:SanitizeTest;
>>> public var eventsTest:EventsTest;
>>> public var objectUtilTests:ObjectUtilsTest;
>>> - public var functionelTests:FunctionalTests;
>>> + public var functionalTests:FunctionalTests;
>>>
>>> }
>>> }
>>> diff --git a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
>>> index 022c4da..75f320c 100644
>>> --- a/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
>>> +++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/FunctionalTests.as
>>> @@ -25,6 +25,8 @@ package flexUnitTests
>>> import org.apache.royale.test.asserts.*;
>>> import org.apache.royale.test.async.*;
>>> import org.apache.royale.utils.functional.*;
>>> + import org.apache.royale.utils.functional.animateFunction;
>>> + import org.apache.royale.test.asserts.assertTrue;
>>>
>>> public class FunctionalTests
>>> {
>>> @@ -192,6 +194,38 @@ package flexUnitTests
>>> assertEquals(value,7,"value should be 7");
>>> }, 300);
>>> }
>>> + [Test(async,timeout="300")]
>>> + public function testAnimate():void
>>> + {
>>> + var foo:Foo = new Foo();
>>> + var animateThis:Function = animateFunction(foo.increment,20);
>>> + for(var i:int=0;i<30;i++){
>>> + animateThis(1);
>>> + }
>>> + var savedThisValue:Number;
>>> + setTimeout(function():void{
>>> + savedThisValue = foo.value;
>>> + },50);
>>> +
>>> + var value:Number = 0;
>>> + function increment(val:Number):void{
>>> + value+=val;
>>> + }
>>> + var animated:Function = animateFunction(increment,20);
>>> + var savedValue:Number;
>>> + for(i=0;i<30;i++){
>>> + animated(1);
>>> + }
>>> +
>>> + setTimeout(function():void{
>>> + savedValue = value;
>>> + },50);
>>> + Async.delayCall(this, function():void
>>> + {
>>> + assertTrue(savedThisValue<3,"foo value should be 2");
>>> + assertTrue(savedValue<3,"value should be 2");
>>> + }, 300);
>>> + }
>>>
>>>
>>> }
>>
>