2012-02-04 08:58:46 +00:00
|
|
|
'use strict';
|
2012-05-25 11:42:33 +00:00
|
|
|
|
2012-01-07 07:20:02 +00:00
|
|
|
/*@
|
|
|
|
Ox.cache <f> Memoize a function
|
2013-07-09 14:29:14 +00:00
|
|
|
fn <f> function
|
|
|
|
options <o>
|
|
|
|
async <b|false> function is async, last argument must be callback
|
|
|
|
key <f|JSON.stringify> return key for arguments
|
2012-01-07 07:20:02 +00:00
|
|
|
<script>
|
2012-03-29 12:34:13 +00:00
|
|
|
Ox.test.fn = Ox.cache(function(n) { return n * Math.random(); });
|
2012-01-07 07:20:02 +00:00
|
|
|
</script>
|
2012-03-29 12:34:13 +00:00
|
|
|
> Ox.test.fn(10) == Ox.test.fn(10);
|
2012-01-07 07:20:02 +00:00
|
|
|
true
|
2012-03-29 12:34:13 +00:00
|
|
|
> Ox.test.fn(10) == Ox.test.fn.clear()(10);
|
2012-01-07 07:20:02 +00:00
|
|
|
false
|
|
|
|
@*/
|
2012-06-21 11:47:52 +00:00
|
|
|
// TODO: add async test
|
2012-04-03 13:09:05 +00:00
|
|
|
Ox.cache = function(fn, options) {
|
2013-12-01 12:12:07 +00:00
|
|
|
var cache = {}, ret;
|
2013-12-01 12:24:37 +00:00
|
|
|
options = options || {};
|
|
|
|
options.async = options.async || false;
|
|
|
|
options.key = options.key || JSON.stringify;
|
2013-12-01 12:12:07 +00:00
|
|
|
ret = function() {
|
|
|
|
var args = Ox.slice(arguments), key = options.key(args);
|
|
|
|
function callback() {
|
|
|
|
// cache all arguments passed to callback
|
|
|
|
cache[key] = Ox.slice(arguments);
|
|
|
|
// call the original callback
|
|
|
|
Ox.last(args).apply(this, arguments);
|
|
|
|
}
|
|
|
|
if (options.async) {
|
|
|
|
if (!(key in cache)) {
|
|
|
|
// call function with patched callback
|
|
|
|
fn.apply(this, args.slice(0, -1).concat(callback));
|
2012-04-03 13:09:05 +00:00
|
|
|
} else {
|
2013-12-01 12:12:07 +00:00
|
|
|
// call callback with cached arguments
|
|
|
|
setTimeout(function() {
|
|
|
|
callback.apply(this, cache[key]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(key in cache)) {
|
|
|
|
cache[key] = fn.apply(this, args);
|
2012-04-03 13:09:05 +00:00
|
|
|
}
|
2013-12-01 12:12:07 +00:00
|
|
|
return cache[key];
|
|
|
|
}
|
|
|
|
};
|
2012-01-07 07:20:02 +00:00
|
|
|
ret.clear = function() {
|
|
|
|
if (arguments.length == 0) {
|
|
|
|
cache = {};
|
|
|
|
} else {
|
2012-05-19 08:40:59 +00:00
|
|
|
Ox.makeArray(arguments).forEach(function(key) {
|
2012-01-07 07:20:02 +00:00
|
|
|
delete cache[key];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return ret;
|
2012-06-21 11:47:52 +00:00
|
|
|
};
|
2012-01-07 07:20:02 +00:00
|
|
|
return ret;
|
2012-02-04 08:58:46 +00:00
|
|
|
};
|
2012-04-08 12:19:53 +00:00
|
|
|
|
2012-05-24 16:20:22 +00:00
|
|
|
/*@
|
|
|
|
Ox.identity <f> Returns its first argument
|
|
|
|
This can be used as a default iterator
|
2012-05-27 21:14:59 +00:00
|
|
|
> Ox.identity(Infinity)
|
|
|
|
Infinity
|
2012-05-24 16:20:22 +00:00
|
|
|
@*/
|
2012-05-27 16:43:12 +00:00
|
|
|
Ox.identity = function(value) {
|
|
|
|
return value;
|
2012-05-19 08:09:48 +00:00
|
|
|
};
|
|
|
|
|
2012-04-08 12:19:53 +00:00
|
|
|
/*@
|
2012-05-24 16:20:22 +00:00
|
|
|
Ox.noop <f> Returns undefined and calls optional callback without arguments
|
2012-05-27 16:43:12 +00:00
|
|
|
This can be used as a default iterator in an asynchronous loop, or to
|
|
|
|
combine a synchronous and an asynchronous code path.
|
2012-05-27 21:14:59 +00:00
|
|
|
> Ox.noop(1, 2, 3)
|
|
|
|
undefined
|
2012-05-28 13:52:27 +00:00
|
|
|
> Ox.noop(1, 2, 3, function() { Ox.test(arguments.length, 0); })
|
2012-05-27 21:14:59 +00:00
|
|
|
undefined
|
2012-04-08 12:19:53 +00:00
|
|
|
@*/
|
2012-05-27 16:43:12 +00:00
|
|
|
Ox.noop = function() {
|
|
|
|
var callback = Ox.last(arguments);
|
2012-04-08 12:19:53 +00:00
|
|
|
Ox.isFunction(callback) && callback();
|
2012-05-27 16:43:12 +00:00
|
|
|
};
|
2013-07-09 23:27:25 +00:00
|
|
|
|
|
|
|
/*@
|
|
|
|
Ox.queue <f> Queue of asynchronous function calls with cached results
|
2013-10-29 13:34:15 +00:00
|
|
|
The results are cached based on all arguments to `fn`, except the last one,
|
|
|
|
which is the callback.
|
|
|
|
(fn, maxThreads) -> <f> Queue function
|
2013-11-16 15:13:41 +00:00
|
|
|
.cancel <f> Cancels all running function calls
|
|
|
|
.clear <f> Clears the queue
|
|
|
|
.reset <f> Cancels all running function calls and clears the queue
|
2013-10-29 13:34:15 +00:00
|
|
|
fn <f> Queued function
|
|
|
|
maxThreads <n|10> Number of parallel function calls
|
2013-07-09 23:27:25 +00:00
|
|
|
@*/
|
|
|
|
Ox.queue = function(fn, maxThreads) {
|
|
|
|
var maxThreads = maxThreads || 10,
|
2013-11-16 15:13:41 +00:00
|
|
|
processing = [],
|
|
|
|
queued = [],
|
2013-10-29 13:34:15 +00:00
|
|
|
ret = Ox.cache(function() {
|
2013-12-01 12:12:07 +00:00
|
|
|
var args = Ox.slice(arguments);
|
2013-11-16 15:16:04 +00:00
|
|
|
queued.push({args: args, key: getKey(args)});
|
2013-10-29 13:34:15 +00:00
|
|
|
process();
|
2013-11-16 15:16:04 +00:00
|
|
|
}, {async: true, key: getKey}),
|
2013-07-09 23:27:25 +00:00
|
|
|
threads = 0;
|
2013-11-16 15:13:41 +00:00
|
|
|
ret.cancel = function() {
|
2013-11-19 23:55:08 +00:00
|
|
|
threads -= processing.length;
|
2013-11-16 15:13:41 +00:00
|
|
|
processing = [];
|
|
|
|
return ret;
|
|
|
|
};
|
2013-10-29 13:34:15 +00:00
|
|
|
ret.clear = function() {
|
2013-11-19 23:55:08 +00:00
|
|
|
threads = 0;
|
2013-11-16 15:13:41 +00:00
|
|
|
queued = [];
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
ret.reset = function() {
|
|
|
|
return ret.cancel().clear();
|
2013-10-29 13:34:15 +00:00
|
|
|
};
|
2013-11-16 15:13:41 +00:00
|
|
|
function getKey(args) {
|
|
|
|
return JSON.stringify(args.slice(0, -1));
|
|
|
|
}
|
2013-07-09 23:27:25 +00:00
|
|
|
function process() {
|
2013-11-16 15:13:41 +00:00
|
|
|
var n = Math.min(queued.length, maxThreads - threads);
|
2013-07-09 23:27:25 +00:00
|
|
|
if (n) {
|
|
|
|
threads += n;
|
2013-11-16 15:13:41 +00:00
|
|
|
processing = processing.concat(queued.splice(0, n));
|
|
|
|
Ox.parallelForEach(
|
|
|
|
processing,
|
|
|
|
function(value, index, array, callback) {
|
|
|
|
var args = value.args, key = value.key;
|
|
|
|
fn.apply(this, args.slice(0, -1).concat(function(result) {
|
|
|
|
var index = Ox.indexOf(processing, function(value) {
|
|
|
|
return value.key == key;
|
|
|
|
});
|
|
|
|
if (index > -1) {
|
|
|
|
processing.splice(index, 1);
|
|
|
|
args.slice(-1)[0](result);
|
2013-11-19 23:55:08 +00:00
|
|
|
threads--;
|
2013-11-16 15:13:41 +00:00
|
|
|
}
|
|
|
|
callback();
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
process
|
|
|
|
);
|
2013-07-09 23:27:25 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-29 13:34:15 +00:00
|
|
|
return ret;
|
2013-07-09 23:27:25 +00:00
|
|
|
};
|
2013-11-29 20:09:33 +00:00
|
|
|
|
|
|
|
/*@
|
|
|
|
Ox.time <f> Returns the time it takes to execute a given function
|
|
|
|
(fn) -> <n> Time in milliseconds
|
|
|
|
@*/
|
|
|
|
Ox.time = function(fn) {
|
|
|
|
var time = new Date;
|
|
|
|
fn();
|
|
|
|
return new Date() - time;
|
|
|
|
};
|