Giter VIP home page Giter VIP logo

promise-memoize's Introduction

promise-memoize

Build Status NPM version Coverage Status

Memoize promise-returning functions. Includes cache expire and prefetch.

  • When data expire mode enabled, new values are fetched in advance. Cache will be always valid, without "gaps".
    • Prefetch happens only for items in use. Inactive ones will be GC-ed as usual.
  • Errors are not cached
    • You still can enable cache with separate expire time for errors, to avoid specific peak loads. For example, set 120s for good result and 1s on fail.

Install

npm install promise-memoize --save

(*) IE9 and below will require setTimeout polyfill for correct work.

Usage example

// Pseudo code
let db = require('mongoose').createConnection('mongodb://localhost/forum');

function lastPosts(limit) {
  return db.model('Post').find().limit(limit).orderBy('-_id').lean(true).exec(); // <- Promise
}

let cachedLastPosts = require('promise-memoize')(lastPosts, { maxAge: 60000 });

// Later...
cachedLastPosts(10).then(posts => console.log(posts));

API

promiseMemoize(fn [, options]) -> memoizedFn

Memoize function fn.

  • fn(params...) — function, returning a promise (or any "thenable"). It can have any number of arguments, but arguments should be uniquely castable to strings (see below).
  • options — options for memoization (optional)
    • maxAge — an amount of milliseconds it should cache resolved values for (default: Infinity, i.e. cache forever).
    • maxErrorAge — an amount of milliseconds it should cache rejected values for (default: 0, i.e. don't cache).
    • resolve — serialiser to build unique key from fn arguments. (default: simple). Possible values:
      • simple (string) — convert each param to string & join those.
      • json (string) — JSON.stringify each param & join results.
      • function(Array) — custom function, with fn params as array on input
      • [ String, Boolean, 'json', function ] — array with custom functions, specific for each fn param position (text shortcuts as above are allowed).

Return value is a function with the same signature as fn.

Note. How prefetch works.

If maxAge used and request to cached data happens after 0.7 * maxAge time, then:

  • cached data returned
  • fn call is executed in parallel
  • cached data will be substituted with new one on success, timeouts will be extended.

So your application will not have to wait for data fetch after cache expire.

memoizedFn(params...) -> promise

Returns result as cached promise (errors are not cached by default). If maxAge used, tries to prefetch new value before expire to replace cache transparently.

memoizedFn.clear()

Remove all cached data.

License

MIT

promise-memoize's People

Contributors

forivall avatar rlidwka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

promise-memoize's Issues

Cache not cleared, promises not resolved

Hi, I've spent quite some time debugging an issue in my app and it seems that there is a problem caused by memoization, and I would greatly appreciate any help to dig deeper.

My app runs on NodeJS 9.5. The main issue is that a memoized function never resolves, e.g.:

// getUser is memoized
// this does not log anything
getUser(email)
    .then((user) => console.log('Got user: ', user))
    .catch((err) => console.log('Got error: ', err))

The issue does not occur constantly and kicks in only after the app is running for some time.

Upon further investigation (debugging of memoizedFn), I noticed that the values from cache are not cleared after a specified timeout (10sec). When I clean the cache manually all works well for some time. E.g.:

  • There are caches for 4 keys A, B, C, and D;
  • When I try to call the function with any of the above 4 keys the promise in never resolved nor rejected;
  • I manually delete key C.
  • When I run the function with param C all works as expected, the caching is happening and after 10 sec the C key is removed.
  • The issue with A, B, and D persists as they are still in the cache, not removed automatically, and not resolving promises.

I understand that that data I provided may be insufficient to identify the issue, but maybe you can suggest me where to look for more? For example, how can I compare what is the difference between a "healthy" key C and dead keys A, B, D?

request: ability to disable prefetch

Thanks for this library!

I am using this library in an AWS Lambda, and ran into an interesting bug caused by the prefetch behavior. My app has code that looks like this:

async function doGetCredentials() {
  // ...async retrieval of credentials that expire after 1 hour...
  return credentials;
}

// memoize credentials while they are valid
const getCredentials = promiseMemoize(doGetCredentials, { maxAge: credentialExpiryAge } );

getCredentials().then(doStuff).then(returnResponse);

The issue I encountered is that AWS Lambdas can be immediately paused (i.e. JS execution stops) as soon as a response is returned, and then re-animated later to handle future requests. This can lead to a scenario where:

  1. request comes in, doGetCredentials is called and the result is memoized
  2. another request comes when the memoized result is "almost expired", so the prefetch request is triggered, but the memoized value is returned.
  3. my code returns a response without waiting for the prefetch request to complete, so Lambda execution is frozen. This happens after the credential request has been made, but before the doGetCredentials promise is fully resolved (i.e. before the refreshed value is memoized).
  4. another request comes in some time later, so Lambda execution resumes. finally the prefetch promise resolves, and the result is cached for maxAge. But the credentials being saved are from some time ago, so they could be already expired (or almost expired). Subsequent requests can get expired credentials.

I think the cleanest fix to this problem would be to just disable the prefetching behavior, so that my response always waits for the credential request to complete, and Lambda can't pause execution mid-way through the credential request.

Check if key has been cached

I'm using this little lib a lot and find it extremely useful. However, I often find myself wanting to check whether or not something has already been received or not and let my program flow depend on that. Any chance this can be polled? A simple .has([args]) method would make me very happy.

0.1.0+ tails

  • better resolvers tests
  • improve memoize coverage
  • unref timers

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.