Yann Eves
Yann Eves

Reputation: 337

Combining _.memoize and _.throttle to limit function calls and cache a result within a time window?

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

Answers (3)

Jordi Teddy
Jordi Teddy

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);

http://jsfiddle.net/6kq4rt0b/

Upvotes: 0

lastoneisbearfood
lastoneisbearfood

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

user77838
user77838

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

Related Questions