function isArray (arg) { return Object.prototype.toString.call(arg).slice(8, -1) === 'Array'; } /** * [chunk Array chunk iteration] * @param { array } arr [array to iterate] * @param {function} fn [function to process array] * @param {integer } intv [iteration interval milliseconds] * @param {function} success [function for async invocation when iteration complete] * @param { object } ctx [context to invoke the function] * * usage: * chunk([1, 2, 3], function(e, i, a){ * console.log(i, e); * }); */ function chunk(arr, fn, intv, success, ctx) { var copy, idx; if(!isArray(arr)) { console.log('Error: chunk - parameter 1 must be an array.'); return; } if(!arr.length) { console.log('Warning: chunk - parameter 1 must be an array with at least one element.'); return; } if(typeof fn !== 'function') { console.log('Error: chunk - parameter 2 must be a function.'); return; } if(intv != null && (typeof intv !== 'number' || intv !== ~~intv)) { console.log('Error: chunk - parameter 3 must be an integer.'); return; } if(success != null && typeof success !== 'function') { console.log('Error: chunk - parameter 4 must be a function for async invocation.'); return; } if(ctx != null && typeof ctx !== 'object') { console.log('Error: chunk - parameter 5 must be an object.'); return; } copy = arr.slice(); idx = 0; intv = intv || 100; intv = intv > 0 ? ~~intv : 0; // get the positve integer interval milliseconds setTimeout(function f() { var itm = copy.shift(); fn.call(ctx, itm, idx++, arr); if(copy.length > 0) { setTimeout(f, intv); } else if(typeof success === 'function') { // used for async invocation success(); } }, 0); } /** * [throttle Function throttle] * @param {function} fn [invoked function to throttle] * @param {integer } intv [interval milliseconds] * @param { array } params [parameters passed into the invoked function] * @param { object } ctx [context to invoke the function] * * usage: * document.addEventListener('click', function(e){ * throttle(doSomething, 1000, e.target.nodeType); * throttle(doSomething, 1000, e.target.childNodes); * throttle(doSomething, 1000, [null]); * }); * function doSomething(data){ * console.log(data); * } */ function throttle (fn, intv, params, ctx) { if(typeof fn !== 'function') { console.log('Error: throttle - parameter 1 must be a function.'); return; } if(intv != null && typeof intv !== 'number') { console.log('Error: throttle - parameter 2 must be a number.'); return; } if(ctx != null && typeof ctx !== 'object') { console.log('Error: throttle - parameter 4 must be an object.'); return; } clearTimeout(fn.tId); intv = intv || 100; if(params == null) params = []; if(!isArray(params)) { if(typeof params.length === 'number' && typeof params[0] !== 'undefined') { params = Array.prototype.slice.call(params); } else { params = [params]; } } fn.tId = setTimeout(function () { fn.apply(ctx, params); }, intv); } /** * [advDuff Iterator for large data] * @param { object } arrLike [object has length property to access elements] * @param {Function} fn [function to process arrLike] * * usage: * advDuf([1,2,3], (e, i) => {console.log(i, e);}); * advDuff(document.querySelectorAll('li'), (e, i) => {console.log(i, e);}); */ function advDuff (arrLike, fn) { var len, trav, left, i; if(typeof arrLike !== 'object' || typeof arrLike.length !== 'number') { console.log('Error: advDuff - parameter 1 must be an array or array-like object'); return; } if(!arrLike.length) return; if(typeof fn !== 'function') { console.log('Error: advDuff - parameter 2 must be a function to process the parameter 1.'); return; } len = arrLike.length; trav = ~~(len / 8); // get the integer turns of iteration left = len % 8; // get the remainder of turns i = 0; if(left> 0) { do{ fn(arrLike[i++], i, arrLike); } while (--left>0); } if(!trav) return; do{ fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); fn(arrLike[i++], i, arrLike); } while (--trav > 0); } /** * [trampoline To optimize the recursion] * @param {function} fn [function to recurse] * @return { any } [final result of recursive function] * * usage: * function fibonacci (n, ac1 = 1, ac2 = 1) { * return n <= 1 ? ac2 : () => fibonacci(n - 1, ac2, ac1 + ac2); * } * trampoline(fibonacci(1000, 1, 1)); */ function trampoline (fn) { while (fn && fn instanceof Function) { fn = fn(); } return fn; } /** * [tail Tail-call optimizing function] * @param {function} fn [function to recurse] * @return { any } [final result of recursive function] * * usage: * var fibonacci = tail((n, ac1 = 1, ac2 = 1) => { * return n <= 1 ? ac2 : fibonacci(n - 1, ac2, ac1 + ac2); * }); * fibonacci(1000, 1, 1); */ function tail (fn) { var value, active = false, stack = []; return function () { stack.push(arguments); if(!active) { active = true; while (stack.length) { value = fn.apply(this, stack.shift()); } active = false; return value; } }; }