code: function memoize(task, expire, hasher) {
      var memo = memoizeTask.memo = {};
      expire -= 0;
      var timers = {};
      if (!hasher) hasher = JSON.stringify.bind(JSON);
      return memoizeTask;
  
      function memoizeTask(value) {
        return waterfall([hasher, readCache]).call(this, value);
  
        // read previous result from cache
        function readCache(hash) {
          if (hash in memo) return memo[hash];
          return waterfall([task, writeCache]).call(this, value);
  
          // write new result to cache
          function writeCache(result) {
            result = memo[hash] = resolve(result);
            if (expire) {
              // cancel previous timer
              if (timers[hash]) clearTimeout(timers[hash]);
              // add new timer
              timers[hash] = setTimeout(clearCache, expire);
            }
            return result;
          }
  
          // clear expired result
          function clearCache() {
            delete memo[hash];
            delete timers[hash];
          }
        }
      }
    }
docs: creates a promise-returning function which caches a result of task.
  
  The cache of result is exposed as the memo property of the function returned by memoize.
  
  @class promisen
  @static
  @function memoize
  @param task {Function} task to wrap
  @param expire {Number} millisecond until cache expired
  @param [hasher] {Function} default: JSON.stringify()
  @returns {Function} promise-returning function
  @example
  var promisen = require("promisen");
  var request = require("request");
  
  var ajaxGet = promisen.denodeify(request);
  var cachedAjax = promisen.memoize(ajaxGet);
  
  var req = {url: "http://www.example.com/"};
  cachedAjax(req).then(function(res) {...}); // res.body contains response body