Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Y.throttle functions cannot be invoked within throttle-period of being created #1801

Open
wants to merge 1 commit into
base: dev-3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/yui-throttle/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ YUI Throttle Change History
@VERSION@
------

* No changes.
* Switched throttle() to be able to call the function it throttles within
the first period after it is setup (@dobedobedoh).

3.16.0
------
Expand Down
28 changes: 23 additions & 5 deletions src/yui-throttle/js/throttle.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,31 @@ to the `Y` object and is <a href="../classes/YUI.html#method_throttle">documente
/*! Based on work by Simon Willison: http://gist.github.com/292562 */
/**
* Throttles a call to a method based on the time between calls.
*
* On the first call to the throttled function the time is recorded,
* and the wrapped function is then executed.
* Subsequent calls to the throttled function will be ignored until
* the throttle time has expired.
*
* The following example will result in precisely one call to the `doSomething` function:
*
* var throttledFn = Y.throttle(doSomething);
* throttledFn(); // This call with result in doSomething being executed.
* throttledFn(); // This call will be ignored.
*
*
* @method throttle
* @for YUI
* @param fn {function} The function call to throttle.
* @param ms {Number} The number of milliseconds to throttle the method call.
* Can set globally with Y.config.throttleTime or by call. Passing a -1 will
* disable the throttle. Defaults to 150.
* @return {function} Returns a wrapped function that calls fn throttled.
* @param [ms=150] {Number} The number of milliseconds to throttle the method call.
* Passing a value of `-1` will disable the throttle.
* A default value can be set globally in `Y.config.throttleTime`.
* If no value is passed, and no global setting was specified in
* `Y.config.throttleTime`, then a default value of 150 milliseconds
* is used.
*
* @return {function} Returns a wrapped function that calls the
* function specified in `fn` with throttling applied.
* @since 3.1.0
*/
Y.throttle = function(fn, ms) {
Expand All @@ -37,7 +55,7 @@ Y.throttle = function(fn, ms) {
};
}

var last = Y.Lang.now();
var last = 0;

return function() {
var now = Y.Lang.now();
Expand Down
72 changes: 51 additions & 21 deletions src/yui-throttle/tests/unit/assets/throttle-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ YUI.add('throttle-tests', function(Y) {
Y.Lang.now = this.originalNow;
},

test_immediate_throttle: function() {
var throttledFn,
hasRun = false;

throttledFn = Y.throttle(function() {
hasRun = true;
}, 150);

Assert.isFalse(hasRun);
throttledFn();
Assert.isTrue(hasRun);
},

test_throttle: function() {
var counter = 0,
out = 0,
Expand All @@ -42,17 +55,17 @@ YUI.add('throttle-tests', function(Y) {
}

// Y.Lang.now() returns always previousTime + 1, so if we throttle
// the function by 75ms and we call the returned function 76 times
// the throttled function should be called once
throttledFn = Y.throttle(count, 75);
// the function by 100ms and we call the returned function 50 times
// the throttled function should be called once.
throttledFn = Y.throttle(count, 100);

// Calling the result function 100 times means the throttled function
// will be called once. So `out` will be 100 and `counter` will be 1
for (i; i < 100; i++) {
// Calling the result function 50 times means the throttled function
// will be called once. So `out` will be 50 and `counter` will be 1
for (i; i < 50; i++) {
out++;
throttledFn();
}

Assert.isFunction(throttledFn, 'Y.throttle failed to return a function');
Assert.areNotSame(count, throttledFn, 'Y.throttle failed to return a new function');
Assert.isTrue(counter < out, 'Y.throttle did not throttle the function call');
Expand All @@ -74,7 +87,7 @@ YUI.add('throttle-tests', function(Y) {
out++;
throttledFn();
}

Assert.isFunction(throttledFn, 'Y.throttle failed to return a function');
Assert.areNotSame(count, throttledFn, 'Y.throttle failed to return a new function');
Assert.areEqual(out, counter, 'Y.throttle DID throttle the function call');
Expand All @@ -96,18 +109,6 @@ YUI.add('throttle-tests', function(Y) {
Assert.isFalse(counter < 1, 'throttled function with no throttle time never called');
Assert.isFalse(counter > 1, 'throttled function with no throttle time called more than once');
},
'test throttled function never called after throttle time': function () {
var counter = 0,
fn = Y.throttle(function () {
counter++;
}, 10);

// call once before the 10ms threshold (as if it were only 5ms)
this.tDelta = 5;
fn();

Assert.areEqual(0, counter, 'throttled function called before throttle time');
},
'`this` is not modified in throttled function': function () {
var counter = 0,
obj = {
Expand All @@ -120,7 +121,6 @@ YUI.add('throttle-tests', function(Y) {
// Adjust delta to be larger the throttle time so the function is called
this.tDelta = 10;
obj.fn();
obj.fn();

// Ensure the test function is called and we do not get a false positive
Assert.areSame(1, counter, 'throttled function was never called');
Expand All @@ -133,6 +133,36 @@ YUI.add('throttle-tests', function(Y) {
};

obj.fn();
},

'test function is invoked when called immediately after setup': function() {
var throttledFn,
runCount = 0;

// Create a throttled function with a delay of 200ms.
throttledFn = Y.throttle(function(args) {
runCount++;
}, 100, true);

// Get the current time, and then immediately call the function.
this.tDelta = 1;
throttledFn();

// Should have happened initially.
Assert.areSame(1, runCount, 'The throttled function was not called');

// Now ensure that the function is invoked after the throttle limit has expired since we set it up.
this.tDelta = 100;
Y.Lang.now();
throttledFn();

Y.later(200, this, function() {
this.resume(function () {
Assert.areSame(2, runCount, 'throttled function was not executed the expected number of times');
});
});

this.wait();
}
}));

Expand Down