HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Caching/memoizing promise results

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
memoizingcachingresultspromise

Problem

I'm wondering what the best approach might be.

One approach is to chain cachedPromise and "regular", thus, if cachedPromise fails, we call a regular one (and caching results).

The second is to have a wrapper function, something like a decorator in python, and being able to wrap any promise-returning function when needed.

Here's code illustrating both approaches:

// this is for node.js environment
// in modern browsers Promise is already implemented 
// as a standard feature
var Promise = require('bluebird');

var cachedPromise = {
    _cache: {},
    get: function(key) {
        var isCached = this._cache.hasOwnProperty(key);
        var data = this._cache[key];
        return new Promise(function(resolve, reject) {
            isCached ? resolve(data) : reject();
        });
    }, 
    store: function(key, value) {
        this._cache[key] = value;   
    }
}

var WrappedPromise = function(promiseFunc) {
    var cache = {};
    return function(key) {
        if (cache.hasOwnProperty(key)) {
            return Promise.cast(cache[key]);
        } else {
            var promise = promiseFunc(key);
            return promise.then(function(data) {
                cache[key] = data;
                return promise;
            });
        }
    }
}

var mockRequestPromise = function(name){
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve({'name': name});
        }, 1000);
    });
}

var requestChained = function(name) {
    return cachedPromise.get(name).catch(function() {
        var promise = mockRequestPromise(name);
        return promise.then(function(data) {
                   cachedPromise.store(name, data);
                   return promise;
               })
    });
}

var requestWrapped = WrappedPromise(mockRequestPromise);


If you have any considerations regarding this issue, I will be much obliged.

Solution

Code looks fine - but it strikes me as somewhat unnecessary.

A promise can only be resolved/rejected once, so if you just keep the promise object itself around, its result is automatically cached within. I.e. if you call .then() on an already-resolved/already-rejected promise object, it'll just immediately invoke your handler(s) with whatever data it was originally resolved/rejected. It won't re-do whatever caused it to be resolved/rejected. So just keep the promise object itself around and you can use it whenever you want.

In order words, caching promise objects makes sense (e.g. caching web request promises), but caching promise results is built-in and happens automatically.

So to that end, it'd be smarter to add caching at a higher level. I.e. don't wrap generic promises, instead wrap specific tasks like network requests and such where caching the promise is desired and you can have more control over what keys to use, when to invalidate the cache etc..

I'd do something like this

// *specialized* function for, say, network requests,
// where caching is desired. In this case, we're caching
// promises by the URL they're fetching
var fetch = (function () {
  var cache = {};
  return function (url) {
    cache[url] = cache.hasOwnProperty(url) ? cache[url] : new Promise(/* request magic goes here */);
    return cache[url];
  };
});


(Obviously, you'll want a way to invalidate the cache etc., this is just to illustrate the point)

Code Snippets

// *specialized* function for, say, network requests,
// where caching is desired. In this case, we're caching
// promises by the URL they're fetching
var fetch = (function () {
  var cache = {};
  return function (url) {
    cache[url] = cache.hasOwnProperty(url) ? cache[url] : new Promise(/* request magic goes here */);
    return cache[url];
  };
});

Context

StackExchange Code Review Q#51651, answer score: 16

Revisions (0)

No revisions yet.