docs: function asyncMemoize(fn, context, hasher) {
      var caches = callProxy.cache = {},
          callbackLists = {}
  
      if (typeof context === "function") {
          hasher = context
          context = null
      }
  
      if (typeof hasher === "undefined") {
          hasher = defaultHasher
      }
  
      return callProxy
  
      function callProxy() {
          var args = [].slice.call(arguments),
              cb = args.pop(),
              key = hasher.apply(null, args)
  
          if (caches[key]) {
              return typeof cb === "function" && cb.apply(null, caches[key])
          } else if (callbackLists[key]) {
              return callbackLists[key].push(cb)
          }
  
          callbackLists[key] = [cb]
  
          args.push(callbackProxy)
  
          fn.apply(context, args)
  
          function callbackProxy() {
              caches[key] = arguments
              var list = callbackLists[key]
              delete callbackLists[key]
              // it might undefined >_< if the callback is blocking
              list && list.forEach(function (cb) {
                  typeof cb === "function" && cb.apply(this, caches[key])
              }, this)
          }
      }
  }