Speed up Node.JS functions by memoizing them, even if asynchronous
JavaScript
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
test
.gitignore
.npmignore
AUTHORS
LICENSE
README.md
package.json

README.md

lru-memoize

Speed up Node.JS functions with memoization, even when asynchronous

Usage

Memoization is a technique to speed up functions, by using a cache, without altering the function or calling it differently.

var memoize = require('lru-memoize');

function reallySlow(foo, bar) {
   /* ... */
}

var reallyFast = memoize(reallySlow);

reallyFast(10, 20);
reallyFast(10, 20);  // faster!

Use this when:

  • You have a function you want to speed up
  • It is called repeatedly during its lifetime with the same parameters
  • These identical calls tend to happen around the same time
  • The function is more or less pure; that is, the results depend entirely on the arguments. (You can fudge this if you accept slightly out of date results; see below for cache options)
  • You can afford to burn more memory storing results
  • You don't need the cache to persist between runs of Node.js

Note that the cache is right in the node process, and thus complex data structures do not need to be serialized, and can even share objects. This can make it much faster or more convenient than using an external cache server like memcached.

The underlying cache is a least-recently-used (LRU) cache. The cache tries to use a constant amount of memory, and when necessary throws away old results that haven't been requested in a while.

Options

You can set options for how the underlying LRU cache should work, with a second parameter. See the documentation for node-lru-cache for details.

var reallyFast = memoize(reallySlow, { maxAge: 1000 * 60 * 60 });

Asynchronous functions

There is one additional parameter not specified by node-lru-cache, which allows for results to be passed to a callback instead. This is useful if your slow function does IO, and thus becomes asynchronous. The convention of using the callback as the last argument is assumed.

function countLines(filename, extension, next) {
  var count = 0;
  fs.createReadStream(filename + '.' + extension)
    .on('data', function(chunk) {
      for (var i=0; i < chunk.length; ++i) {
        if (chunk[i] == 10) {
          count++;
        }
      }
    })
    .on('end', function() {
      next(count);
    });
}

function cb(results) {
  console.log(results);
}

countLines('myfile', 'txt', cb);

memoized_countLines = memoize(countLines, { next: true });

memoized_countLines('myfile', 'txt', cb);
memoized_countLines('myfile', 'txt', cb); // faster!

// The last argument isn't part of the memoization, so you can alter it
memoized_countLines('myfile', 'txt', someOtherCb); // still fast!

Caveats

This module does not work with functions which rely on 'this' context, or which take functions themselves.

The function arguments should be cleanly stringifiable.

Functions which use arguments that contain the control character '\001' may have surprising results.

If you return a data structure from the memoized function, and then modify that structure, you'll modify the cached value as well.