memoize.js
 1  'use strict';
 2  
 3  Object.defineProperty(exports, "__esModule", {
 4      value: true
 5  });
 6  exports.default = memoize;
 7  
 8  var _setImmediate = require('./internal/setImmediate');
 9  
10  var _setImmediate2 = _interopRequireDefault(_setImmediate);
11  
12  var _initialParams = require('./internal/initialParams');
13  
14  var _initialParams2 = _interopRequireDefault(_initialParams);
15  
16  var _wrapAsync = require('./internal/wrapAsync');
17  
18  var _wrapAsync2 = _interopRequireDefault(_wrapAsync);
19  
20  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21  
22  /**
23   * Caches the results of an async function. When creating a hash to store
24   * function results against, the callback is omitted from the hash and an
25   * optional hash function can be used.
26   *
27   * **Note: if the async function errs, the result will not be cached and
28   * subsequent calls will call the wrapped function.**
29   *
30   * If no hash function is specified, the first argument is used as a hash key,
31   * which may work reasonably if it is a string or a data type that converts to a
32   * distinct string. Note that objects and arrays will not behave reasonably.
33   * Neither will cases where the other arguments are significant. In such cases,
34   * specify your own hash function.
35   *
36   * The cache of results is exposed as the `memo` property of the function
37   * returned by `memoize`.
38   *
39   * @name memoize
40   * @static
41   * @memberOf module:Utils
42   * @method
43   * @category Util
44   * @param {AsyncFunction} fn - The async function to proxy and cache results from.
45   * @param {Function} hasher - An optional function for generating a custom hash
46   * for storing results. It has all the arguments applied to it apart from the
47   * callback, and must be synchronous.
48   * @returns {AsyncFunction} a memoized version of `fn`
49   * @example
50   *
51   * var slow_fn = function(name, callback) {
52   *     // do something
53   *     callback(null, result);
54   * };
55   * var fn = async.memoize(slow_fn);
56   *
57   * // fn can now be used as if it were slow_fn
58   * fn('some name', function() {
59   *     // callback
60   * });
61   */
62  function memoize(fn, hasher = v => v) {
63      var memo = Object.create(null);
64      var queues = Object.create(null);
65      var _fn = (0, _wrapAsync2.default)(fn);
66      var memoized = (0, _initialParams2.default)((args, callback) => {
67          var key = hasher(...args);
68          if (key in memo) {
69              (0, _setImmediate2.default)(() => callback(null, ...memo[key]));
70          } else if (key in queues) {
71              queues[key].push(callback);
72          } else {
73              queues[key] = [callback];
74              _fn(...args, (err, ...resultArgs) => {
75                  // #1465 don't memoize if an error occurred
76                  if (!err) {
77                      memo[key] = resultArgs;
78                  }
79                  var q = queues[key];
80                  delete queues[key];
81                  for (var i = 0, l = q.length; i < l; i++) {
82                      q[i](err, ...resultArgs);
83                  }
84              });
85          }
86      });
87      memoized.memo = memo;
88      memoized.unmemoized = fn;
89      return memoized;
90  }
91  module.exports = exports['default'];