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

Overview

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

Comments
  • request: ability to disable prefetch

    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.

    opened by gavinsharp 9
  • Check if key has been cached

    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.

    opened by kasvtv 8
  • Allow function's

    Allow function's "this" scope to be referenced

    Not sure if this is the correct approach but I needed to memoize functions of a class and they needed to refer to "this".

    The change works for me and is only adds a new option "fnthis" (better names more than welcome).

    opened by asplinsol 2
  • Update memoize.js

    Update memoize.js

    In environments where the event loop takes longer to return than the timeout interval (this is possible with very short timeout intervals or very long running synchronous functions), it's possible for destroyCacheObj to run before askPrefetch, which creates a situation where askPrefetch will crash (and, at least in my case, take the entire application with it).

    bug 
    opened by cube-drone 2
  • Build with babel into /es5 for older versions of node

    Build with babel into /es5 for older versions of node

    Support older versions of node (0.10) via transpiling into /es5 with babel, so, with older versions of node, you just require with

    var promiseMemoize = require('promise-memoize/es5')
    

    grumble grumble vendor still hasn't upgraded off of 0.10

    opened by forivall 2
  • Remove extraneous paths from scripts

    Remove extraneous paths from scripts

    from https://docs.npmjs.com/cli/run-script

    In addition to the shell's pre-existing PATH, npm run adds node_modules/.bin to the PATH provided to scripts.

    opened by forivall 1
  • Cache not cleared, promises not resolved

    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?

    opened by Uko 2
Owner
Nodeca
rcopen.com sources and node.js libraries
Nodeca
:bird: :zap: Bluebird is a full featured promise library with unmatched performance.

Got a question? Join us on stackoverflow, the mailing list or chat on IRC Introduction Bluebird is a fully featured promise library with focus on inno

Petka Antonov 20.2k Jan 5, 2023
:bird: :zap: Bluebird is a full featured promise library with unmatched performance.

Got a question? Join us on stackoverflow, the mailing list or chat on IRC Introduction Bluebird is a fully featured promise library with focus on inno

Petka Antonov 20.2k Dec 31, 2022
Delay a promise a specified amount of time

delay Delay a promise a specified amount of time If you target Node.js 15 or later, you can do await require('timers/promises').setTimeout(1000) inste

Sindre Sorhus 518 Dec 26, 2022
Promise ponyfill with pinkie

pinkie-promise ES2015 Promise ponyfill Module exports global Promise object (if available) or pinkie Promise polyfill. Install $ npm install --save pi

Vsevolod Strukchinsky 120 Jan 16, 2022
🚀 Tiny goodies for Continuation-Passing-Style functions, fully tested

// ) ) ___ ___ ___ __//__ // ) ) // ) ) (( ) ) // // / / // //___/ / \ \ // ((___/ / ((___

Dmitri Zaitsev 64 Nov 20, 2022
Async utilities for node and the browser

Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript. Although originally designed f

Caolan McMahon 27.8k Dec 31, 2022
A solid, fast Promises/A+ and when() implementation, plus other async goodies.

when.js When.js is a rock solid, battle-tested Promises/A+ and when() implementation, including a complete ES6 Promise shim. It's a powerful combinati

The Javascript Architectural Toolkit 3.4k Dec 18, 2022
Flow control and error handling for Node.js

NOTE: This project is deprecated and no longer being actively developed or maintained. See Issue #50 for details. StrongLoop zone library Overview The

StrongLoop and IBM API Connect 280 Feb 18, 2022
Helps you write libraries that accept both promises and callbacks.

What is it? promise-breaker makes it easy to write functions that will accept an optional callback, or return a Promise if a callback is not provided.

Jason Walton 83 Aug 11, 2022
Debounce promise-returning & async functions.

perfect-debounce An improved debounce function with Promise support. Well tested debounce implementation Native Promise support Avoid duplicate calls

unjs 55 Jan 2, 2023
A remote nodejs Cache Server, for you to have your perfect MAP Cache Saved and useable remotely. Easy Server and Client Creations, fast, stores the Cache before stopping and restores it again!

remote-map-cache A remote nodejs Cache Server, for you to have your perfect MAP Cache Saved and useable remotely. Easy Server and Client Creations, fa

Tomato6966 8 Oct 31, 2022
Plain functions for a more functional Deku approach to creating stateless React components, with functional goodies such as compose, memoize, etc... for free.

"Keo" is the Vietnamese translation for glue. Plain functions for a more functional Deku approach to creating stateless React components, with functio

Adam Timberlake 225 Sep 24, 2022
A enhanced web storage with env support, expire time control, change callback and LRU storage clear strategy.

enhanced-web-storage A enhanced web storage with env support, expire time control, change callback and LRU storage clear strategy. How to Start import

Ziwen Mei 15 Sep 10, 2021
Prefetch and sync state to client with one line of code, out-of-the-box

vue3-SSR-starter Prefetch and sync state to client with one line of code, out-of-the-box Features vue3 SSR vue-router we don't need vuex anymore one l

周子贤 36 Aug 28, 2022
Inside-out promise; lets you call resolve and reject from outside the Promise constructor function.

Inside-out promise; lets you call resolve and reject from outside the Promise constructor function.

Lily Scott 3 Feb 28, 2022
Adds promise support (rejects(), doesNotReject()) to tape by decorating it using tape-promise.

Tape With Promises Adds promise support (rejects(), doesNotReject()) to tape by decorating it using tape-promise. Install npm install --save-dev @smal

Small Technology Foundation 3 Mar 21, 2022
A TurboRepo local cache server which uploads artifact cache to GH artifacts.

TurboRepo Github Artifacts action This action allows you use Github artifacts as TurboRepo remote cache server. How it works? It's starts a local Turb

Felix Mosheev 65 Dec 18, 2022
The nestjs cache module based on cache-manager & decorators 🍃

A progressive Node.js framework for building efficient and scalable server-side applications. zirus-cache zirus-cache for Nest.JS - simple and modern

Yakov Bobroff 4 May 9, 2022
Nuxt-Module, that provides a system to set shopware cache-tags for later use in e.g. a full-page cache

nuxt-shopware-caching Nuxt-Module, that provides a system to set shopware cache-tags for later use in e.g. a full-page cache. This module is meant to

Mothership GmbH 5 Nov 8, 2022
Brail is a framework built on NextJS for developing email templates in React, and returning HTML that is compatible with major email clients.

Brail is a framework built on NextJS for developing email templates in React, and returning HTML that is compatible with major email clients. It aims to seperate the concerns of generating the emails and delivering them.

null 121 Jan 2, 2023