From 9bccc9c53c6e10661f552a580db28beb46734b11 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Mon, 3 Dec 2012 01:04:54 -0800 Subject: [PATCH] Ensured `_.throttle` nulls the `timeoutId`. [closes #129] Former-commit-id: 24242f513e01adb2827cc3a5af6c8904098a9280 --- README.md | 1 + doc/README.md | 34 +++++++++++++++--------------- lodash.js | 1 + lodash.min.js | 10 ++++----- lodash.underscore.js | 10 ++++----- test/test.js | 49 ++++++++++++++++++++++++++++++++------------ 6 files changed, 65 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 14e57ab65..c7411f307 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ require({ * Ensure Lo-Dash runs in the JS engine embedded in various Adobe products * Ensured `bound` result of `_.bind(func, …)` is an instance of `bound` and `func` * Ensured `_.reduce` and `_.reduceRight` pass the correct number of `callback` arguments + * Ensured `_.throttle` nulls the `timeoutId` * Made deep `_.clone` more closely follow the structured clone algorithm and copy array properties assigned by `RegExp#exec` * Optimized compiled templates in Firefox * Optimized `_.forEach`, `_.forOwn`, `_.isNumber`, and `_.isString` diff --git a/doc/README.md b/doc/README.md index 3c9086583..291367e04 100644 --- a/doc/README.md +++ b/doc/README.md @@ -698,7 +698,7 @@ The `lodash` function. ### `_.chain(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4073 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4074 "View in source") [Ⓣ][1] Wraps the value in a `lodash` wrapper object. @@ -732,7 +732,7 @@ var youngest = _.chain(stooges) ### `_.tap(value, interceptor)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4098 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4099 "View in source") [Ⓣ][1] Invokes `interceptor` with the `value` as the first argument, and then returns `value`. The purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. @@ -762,7 +762,7 @@ _.chain([1, 2, 3, 200]) ### `_.prototype.chain()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4120 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4121 "View in source") [Ⓣ][1] This function returns the wrapper object. Note: This function is defined to ensure the existing wrapper object is returned, instead of creating a new wrapper object like the `_.chain` method does. @@ -783,7 +783,7 @@ _([1, 2, 3]).value(); ### `_.prototype.toString()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4136 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4137 "View in source") [Ⓣ][1] Produces the `toString` result of the wrapped value. @@ -804,7 +804,7 @@ _([1, 2, 3]).toString(); ### `_.prototype.valueOf()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4152 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4153 "View in source") [Ⓣ][1] Extracts the wrapped value. @@ -1807,7 +1807,7 @@ jQuery(window).on('scroll', throttled); ### `_.wrap(value, wrapper)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3625 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3626 "View in source") [Ⓣ][1] Creates a function that passes `value` to the `wrapper` function as its first argument. Additional arguments passed to the function are appended to those passed to the `wrapper` function. The `wrapper` is executed with the `this` binding of the created function. @@ -2720,7 +2720,7 @@ _.values({ 'one': 1, 'two': 2, 'three': 3 }); ### `_.escape(string)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3649 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3650 "View in source") [Ⓣ][1] Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their corresponding HTML entities. @@ -2744,7 +2744,7 @@ _.escape('Moe, Larry & Curly'); ### `_.identity(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3669 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3670 "View in source") [Ⓣ][1] This function returns the first argument passed to it. Note: This function is used throughout Lo-Dash as a default callback. @@ -2769,7 +2769,7 @@ moe === _.identity(moe); ### `_.mixin(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3695 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3696 "View in source") [Ⓣ][1] Adds functions properties of `object` to the `lodash` function and chainable wrapper. @@ -2799,7 +2799,7 @@ _('curly').capitalize(); ### `_.noConflict()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3721 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3722 "View in source") [Ⓣ][1] Reverts the '_' variable to its previous value and returns a reference to the `lodash` function. @@ -2819,7 +2819,7 @@ var lodash = _.noConflict(); ### `_.random([min=0, max=1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3744 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3745 "View in source") [Ⓣ][1] Produces a random number between `min` and `max` *(inclusive)*. If only one argument is passed, a number between `0` and the given number will be returned. @@ -2847,7 +2847,7 @@ _.random(5); ### `_.result(object, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3782 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3783 "View in source") [Ⓣ][1] Resolves the value of `property` on `object`. If `property` is a function it will be invoked and its result returned, else the property value is returned. If `object` is falsey, then `null` is returned. @@ -2882,7 +2882,7 @@ _.result(object, 'stuff'); ### `_.template(text, data, options)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3867 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3868 "View in source") [Ⓣ][1] A micro-templating method that handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code. Note: In the development build `_.template` utilizes sourceURLs for easier debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp` build and avoiding `_.template` use, or loading Lo-Dash in a sandboxed page. See http://developer.chrome.com/trunk/extensions/sandboxingEval.html @@ -2956,7 +2956,7 @@ fs.writeFileSync(path.join(cwd, 'jst.js'), '\ ### `_.times(n, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3998 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3999 "View in source") [Ⓣ][1] Executes the `callback` function `n` times, returning an array of the results of each `callback` execution. The `callback` is bound to `thisArg` and invoked with one argument; *(index)*. @@ -2988,7 +2988,7 @@ _.times(3, function(n) { this.cast(n); }, mage); ### `_.unescape(string)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4024 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4025 "View in source") [Ⓣ][1] The opposite of `_.escape`, this method converts the HTML entities `&`, `<`, `>`, `"`, and `'` in `string` to their corresponding characters. @@ -3012,7 +3012,7 @@ _.unescape('Moe, Larry & Curly'); ### `_.uniqueId([prefix])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4044 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4045 "View in source") [Ⓣ][1] Generates a unique ID. If `prefix` is passed, the ID will be appended to it. @@ -3046,7 +3046,7 @@ _.uniqueId(); ### `_.VERSION` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4165 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4166 "View in source") [Ⓣ][1] *(String)*: The semantic version number. diff --git a/lodash.js b/lodash.js index 3ddfb544d..b931f4424 100644 --- a/lodash.js +++ b/lodash.js @@ -3591,6 +3591,7 @@ if (remaining <= 0) { clearTimeout(timeoutId); + timeoutId = null; lastCalled = now; result = func.apply(thisArg, args); } diff --git a/lodash.min.js b/lodash.min.js index 9efab5600..d39533c9c 100644 --- a/lodash.min.js +++ b/lodash.min.js @@ -34,8 +34,8 @@ e.length:0;for("number"==typeof n&&(r=(0>n?Dt(0,r+n):Pt(n,r-1))+1);r--;)if(e[r]= function(e,t,n){return t=c(t,n),P(e,function(e,n,r){return!t(e,n,r)})},s.rest=X,s.result=function(e,t){var n=e?e[t]:r;return C(n)?e[t]():n},s.shuffle=function(e){var t=-1,n=Array(e?e.length:0);return wn(e,function(e){var r=St(Ht()*(++t+1));n[t]=n[r],n[r]=e}),n},s.size=function(e){var t=e?e.length:0;return"number"==typeof t?t:bn(e).length},s.some=R,s.sortBy=function(e,t,n){var r=[],t=c(t,n);wn(e,function(e,n,i){r.push({a:t(e,n,i),b:n,c:e})}),e=r.length;for(r.sort(f);e--;)r[e]=r[e].c;return r},s.sortedIndex= V,s.tap=function(e,t){return t(e),e},s.template=function(e,t,n){e||(e=""),n||(n={});var r,i,u=s.templateSettings,a=0,f=n.interpolate||u.interpolate||mt,l="__p+='",c=n.variable||u.variable,h=c;e.replace(RegExp((n.escape||u.escape||mt).source+"|"+f.source+"|"+(f===vt?dt:mt).source+"|"+(n.evaluate||u.evaluate||mt).source+"|$","g"),function(t,n,i,s,o,u){return i||(i=s),l+=e.slice(a,u).replace(yt,p),n&&(l+="'+__e("+n+")+'"),o&&(l+="';"+o+";__p+='"),i&&(l+="'+((__t=("+i+"))==null?'':__t)+'"),r||(r=o||ot .test(n||i)),a=u+t.length,t}),l+="';\n",h||(c="obj",r?l="with("+c+"){"+l+"}":(n=RegExp("(\\(\\s*)"+c+"\\."+c+"\\b","g"),l=l.replace(ht,"$&"+c+".").replace(n,"$1__d"))),l=(r?l.replace(at,""):l).replace(ft,"$1").replace(lt,"$1;"),l="function("+c+"){"+(h?"":c+"||("+c+"={});")+"var __t,__p='',__e=_.escape"+(r?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":(h?"":",__d="+c+"."+c+"||"+c)+";")+l+"return __p}";try{i=o("_","return "+l)(s)}catch(d){throw d.source=l,d}return t?i(t) -:(i.source=l,i)},s.throttle=function(e,t){function n(){a=new Date,u=r,s=e.apply(o,i)}var i,s,o,u,a=0;return function(){var r=new Date,f=t-(r-a);return i=arguments,o=this,0>=f?(clearTimeout(u),a=r,s=e.apply(o,i)):u||(u=setTimeout(n,f)),s}},s.times=function(e,t,n){for(var e=+e||0,r=-1,i=Array(e);++r=l?(clearTimeout(u),u=r,a=f,s=e.apply(o,i)):u||(u=setTimeout(n,l)),s}},s.times=function(e,t,n){for(var e=+e||0,r=-1,i=Array(e);++r=f?(clearTimeout(o),u=a,i=e.apply(s,r)):o||(o=setTimeout(n,f)),i}},n.times=function(e,t,n){for(var e=+e||0 -,r=-1,i=Array(e);++rP(arguments,i,1)&&r.push(i)}return r},n.wrap=function(e,t){return function(){var n=[e];return nt.apply(n,arguments),t.apply(this,n)}},n.zip=function(e){for(var t=-1,n=e?k(L(arguments,"length")):0,r=Array(n);++t=f?(clearTimeout(o),o=null,u=a,i=e.apply(s,r)):o||(o=setTimeout(n,f)),i}},n.times=function(e,t,n){for(var e=+ +e||0,r=-1,i=Array(e);++rP(arguments,i,1)&&r.push(i)}return r},n.wrap=function(e,t){return function(){var n=[e];return nt.apply(n,arguments),t.apply(this,n)}},n.zip=function(e){for(var t=-1,n=e?k(L(arguments,"length")):0,r=Array(n);++t 1); - }); - asyncTest('supports recursive calls', function() { var counter = 0; var throttled = _.throttle(function() { @@ -1801,6 +1789,41 @@ QUnit.start(); }, 260); }); + + asyncTest('should not trigger a trailing call when invoked once', function() { + var counter = 0, + throttled = _.throttle(function() { counter++; }, 32); + + throttled(); + equal(counter, 1); + + setTimeout(function() { + equal(counter, 1); + QUnit.start(); + }, 64); + }); + + asyncTest('should trigger a trailing call when invoked in a loop', function() { + var counter = 0, + limit = 90, + throttled = _.throttle(function() { counter++; }, 32), + start = new Date; + + while ((new Date - start) < limit) { + throttled(); + } + setTimeout(function() { + throttled(); + throttled(); + }, 64); + + setTimeout(function() { + ok(counter > 4); + QUnit.start(); + }, 96); + + ok(counter > 1); + }); }()); /*--------------------------------------------------------------------------*/