Cache pattern with callbacks
I have a class that handles data. Other classes can give it their values and the data class will populate them.
It looks like this:
class Data
constructor : ->
@products = null
populateProducts : (callback)=>
ajaxCall (data)=>
@products = data
callback()
allProducts : (list)=>
if @products?
list = @products
else
@populateProducts => allProducts list
The problem I am facing is that every method I add will have to check if products exists. So I am looking into ways to make that part of the code reusable.
One way I tried was the following:
productsCheck : (callback)=>
if @products?
callback()
else
@populateProducts => products callback
Using this method I could simplify allProducts:
allProducts : (list)=>
@productsCheck => list = @products
In the end I am looking for technique that does: "if products don'开发者_StackOverflow中文版t exists populate them from the database". I was maybe thinking that this might be a known pattern so if that is the case information about it is also appreciated.
Underscore has the concept of _.memoize
Which will cache the result of a function call to an id (i.e. the first argument).
Memoization is an easy pattern to implement.
var memoize = function _memoize(f) {
var cache = {};
return function _intercept(arg) {
if (cache[arg]) return cache[arg];
return (cache[arg] = f.apply(this, arguments));
}
};
You have to adjust this pattern to make it work asynchronously with callbacks.
For complete-ness here is the _.memoize
code :
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
Underscore seems to accept a hasher
as a second argument which will generate a unique key based on the arguments. (the default is underscore identity function).
It also seems to use hasOwnProperty
so you can use annoying keys like toString
and valueOf
which will flag truthy on a property check.
Because you're loading @products
asynchronously, and every method on the object has to (potentially) wait for @products
to load, every method on the object is asynchronous and needs to return its result to a callback. There's no way around this; you can't make asynchronous JS code synchronous. allProducts
can't just return a value.
Here's what you should do: First, change populateProducts
so it won't make an Ajax call if it doesn't have to:
populateProducts : (callback) =>
if @products?
callback()
else ajaxCall (data) =>
@products = data
callback()
Then just start each method that uses @products
with a call to @populateProducts
, like so:
allProducts : (callback) =>
@populateProducts =>
callback @products
firstProduct: (callback) =>
@populateProducts =>
callback @products[0]
# ...
精彩评论