开发者

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]

# ...
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜