oxjs/source/Ox/js/Function.js

230 lines
6.5 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
2012-05-25 11:42:33 +00:00
2012-01-07 07:20:02 +00:00
/*@
Ox.cache <f> Memoize a function
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>
Ox.test.fn = Ox.cache(function(n) { return n * Math.random(); });
2012-01-07 07:20:02 +00:00
</script>
> Ox.test.fn(10) == Ox.test.fn(10);
2012-01-07 07:20:02 +00:00
true
> 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
Ox.cache = function(fn, options) {
2013-12-01 12:12:07 +00:00
var cache = {}, ret;
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));
} 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);
}
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 {
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-04-08 12:19:53 +00:00
2014-02-01 20:35:48 +00:00
/*@
2014-02-01 20:49:19 +00:00
Ox.debounce <f> Runs a function once it stops being called for a given interval
(fn[, ms][, immediate]) -> <f> Debounced function
2014-02-01 20:35:48 +00:00
fn <f> Function to debounce
ms <n|250> Interval in milliseconds
immediate <b|false> If true, function is called once immediately
@*/
Ox.debounce = function(fn/*, ms, immediate*/) {
var args,
immediate = Ox.last(arguments) === true,
ms = Ox.isNumber(arguments[1]) ? arguments[1] : 250,
timeout;
return function() {
args = arguments;
if (!timeout) {
if (immediate) {
fn.apply(null, args);
args = null;
}
} else {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
if (args !== null) {
fn.apply(null, args);
}
timeout = null;
}, ms);
};
};
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
@*/
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
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
@*/
Ox.noop = function() {
var callback = Ox.last(arguments);
2012-04-08 12:19:53 +00:00
Ox.isFunction(callback) && callback();
};
2013-07-09 23:27:25 +00:00
2014-02-01 20:49:19 +00:00
/*@
Ox.once <f> Runs a function once, and then never again
2014-09-23 21:53:41 +00:00
(fn) -> <f> Function that will run only once
2014-02-01 20:49:19 +00:00
fn <f> Function to run once
@*/
Ox.once = function(fn) {
var once = false;
return function() {
if (!once) {
once = true;
2014-08-19 08:18:56 +00:00
fn.apply(null, arguments);
2014-02-01 20:49:19 +00:00
}
};
};
2013-07-09 23:27:25 +00:00
/*@
Ox.queue <f> Queue of asynchronous function calls with cached results
The results are cached based on all arguments to `fn`, except the last one,
which is the callback.
(fn, maxThreads) -> <f> Queue function
.cancel <f> Cancels all running function calls
.clear <f> Clears the queue
.reset <f> Cancels all running function calls and clears the queue
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) {
2014-08-19 08:18:56 +00:00
maxThreads = maxThreads || 10;
var processing = [],
queued = [],
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)});
process();
2013-11-16 15:16:04 +00:00
}, {async: true, key: getKey}),
2013-07-09 23:27:25 +00:00
threads = 0;
ret.cancel = function() {
2013-11-19 23:55:08 +00:00
threads -= processing.length;
processing = [];
return ret;
};
ret.clear = function() {
2013-11-19 23:55:08 +00:00
threads = 0;
queued = [];
return ret;
};
ret.reset = function() {
return ret.cancel().clear();
};
function getKey(args) {
return JSON.stringify(args.slice(0, -1));
}
2013-07-09 23:27:25 +00:00
function process() {
var n = Math.min(queued.length, maxThreads - threads);
2013-07-09 23:27:25 +00:00
if (n) {
threads += n;
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--;
}
callback();
}));
},
process
);
2013-07-09 23:27:25 +00:00
}
}
return ret;
2013-07-09 23:27:25 +00:00
};
2013-11-29 20:09:33 +00:00
2014-02-01 19:45:17 +00:00
/*@
2014-02-01 20:49:19 +00:00
Ox.throttle <f> Runs a function at most once per given interval
2014-02-01 19:45:17 +00:00
(fn[, ms]) -> <f> Throttled function
fn <f> Function to throttle
ms <n|250> Interval in milliseconds
@*/
Ox.throttle = function(fn, ms) {
var args,
timeout;
ms = arguments.length == 1 ? 250 : ms;
return function() {
args = arguments;
if (!timeout) {
2014-02-01 20:35:48 +00:00
fn.apply(null, args);
args = null;
timeout = setTimeout(function() {
if (args !== null) {
fn.apply(null, args);
}
timeout = null;
}, ms);
2014-08-19 08:18:56 +00:00
}
2014-02-01 19:45:17 +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) {
2013-12-06 09:31:33 +00:00
var time = new Date();
2013-11-29 20:09:33 +00:00
fn();
return new Date() - time;
};