Reputation: 337
Using a function that makes a chain of calls to return a result, which is requested several times at once in an Angular template. During those requests, the result will not change, so the overhead of making the extra calls inside the function is unnecessary.
Is it possible to create a throttled function that caches and returns the same result within a certain wait time?
Along the lines of the simplified example below.
var throttleCache = _.memoizeThrottle(function() {
return _.now();
}, 100);
_.delay(throttleCache, 0); // now + 0ms
_.delay(throttleCache, 10); // now + 0ms
_.delay(throttleCache, 99); // now + 0ms
_.delay(throttleCache, 101); // now + 101ms
_.delay(throttleCache, 150); // now + 101ms
As far as I've understood, _.memoize
caches results indefinitely based on the arguments, can't see a way to automatically flush this cache at intervals. And _.throttle
and _.debounce
only trigger the function within the set constraints, but do not return the output of the contained function.
Upvotes: 4
Views: 2370
Reputation: 26
memoize does not expire and throttled returns the same value regardless of arguments.
Here's a combination of throttle and memoize to get a memoized version the function that will be be recomputed after a fixed duration.
// tester to get a combinaison of throttle and memoize.
var _ = require('lodash');
var start = Date.now();
var func = function(text) {
var toDisplay = "argument " + text + " at " + (Date.now() - start);
console.log("executed with", toDisplay);
return toDisplay;
};
var getCachedFunc = function(myFuncToCache, cacheDuration, context) {
// we need to return a different throttled function for each different parameters so memoize it
var memoizedFunction = _.memoize(function() {
var myFuncToCacheArguments = arguments;
var throttledFunc = _.throttle(myFuncToCache, cacheDuration, {trailing: false});
return function executeThrottledFunction() {return throttledFunc.apply(null, myFuncToCacheArguments);};
});
return function applyMemoizedFunction() {
// apply the throttled function
return memoizedFunction.apply(context, arguments)();
};
};
var myCachedFunc = getCachedFunc(func, 4000);
var callWithArgument1 = function() {
console.log("calling with argument 1 at " + (Date.now() - start));
console.log("returned",myCachedFunc('1'));
};
var callWithArgument2 = function() {
console.log("calling with argument 2 at " + (Date.now() - start));
console.log("returned",myCachedFunc('2'));
};
callWithArgument1();
setTimeout(function() {callWithArgument1();}, 2000);
setTimeout(function() {callWithArgument2();}, 2200);
setTimeout(function() {callWithArgument1();}, 5000);
Upvotes: 0
Reputation: 4125
According to the underscorejs source , it looks like both _.throttle and _.debounce DO return the result of the last computation. So as is, they already do what you want.
Upvotes: 0
Reputation:
I used the _.memoize method to extend it and add a ttl argument, to force recomputing the values when expired.
#!/usr/bin/env coffee
_=require 'underscore'
# Memoize an expensive function by storing its results.
# After ttl time, the value will be recomputed
memoinesic = (func, hasher=_.identity, ttl=0)->
memoize = ()->
cache = memoize.cache
key = hasher.apply(@,arguments)
now = Date.now()
if !_.has(cache,key) or now>cache[key].expires
cache[key] =
value: func.apply(@,arguments)
expires: now+ttl
cache[key].value
memoize.cache = {}
memoize
_.mixin(memoinesic:memoinesic)
# Let's try it!
appendToNow = _.memoinesic(
(x)-> "#{x}:#{Date.now()}",
null,
1000
)
logTimedFoo = _.compose console.log,_.partial appendToNow,'foo'
logTimedFoo()
setInterval logTimedFoo,200
Upvotes: 2