'use strict'; (function() { function asyncMap(forEach, collection, iterator, that, callback) { var type = Ox.typeOf(collection), results = type == 'object' ? {} : []; callback = Ox.last(arguments); that = arguments.length == 5 ? that : null; forEach(collection, function(value, key, collection, callback) { iterator(value, key, collection, function(value) { results[key] = value; callback(); }); }, that, function() { callback(type == 'string' ? results.join('') : results); }); }; Ox.asyncMap = function(array, iterator, that, callback) { array = Ox.makeArray(array); callback = Ox.last(arguments); that = arguments.length == 4 ? that : null; if (array.some(Ox.isArray)) { Ox.serialMap(array, function(value, key, array, callback) { Ox.parallelMap(Ox.makeArray(value), iterator, callback); }, callback); } else { Ox.parallelMap(array, iterator, callback); } }; /*@ Ox.nonblockingForEach Non-blocking `forEach` with synchronous iterator (col, iterator[, that], callback[, ms]) -> undefined collection Collection iterator Iterator function value <*> Value key Key collection The collection that The iterator's `this` binding callback Callback function ms Number of milliseconds after which to insert a `setTimeout` call @*/ Ox.nonblockingForEach = function(collection, iterator, that, callback, ms) { var i = 0, keys, last = Ox.last(arguments), n, time, type = Ox.typeOf(collection); callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; collection = type == 'array' || type == 'object' ? collection : Ox.toArray(collection); keys = type == 'object' ? Object.keys(collection) : Ox.range(collection.length); ms = ms || 1000; n = Ox.len(collection); that = arguments.length == 5 || ( arguments.length == 4 && Ox.isFunction(last) ) ? that : null; time = +new Date(); iterate(); function iterate() { Ox.forEach(keys.slice(i), function(key) { if (key in collection) { if (iterator.call( that, collection[key], key, collection ) === false) { i = n; return false; } } i++; if (+new Date() >= time + ms) { return false; // break } }); if (i < n) { setTimeout(function() { time = +new Date(); iterate(); }, 1); } else { callback(); } } }; /*@ Ox.nonblockingMap Non-blocking `map` with synchronous iterator (collection, iterator[, that], callback[, ms]) -> undefined collection Collection iterator Iterator function that The iterator's `this` binding callback Callback function ms Number of milliseconds after which to insert a `setTimeout` call > Ox.nonblockingMap(Ox.range(100000), Ox.identity, function(r) { Ox.test(r.length, 100000); }) undefined @*/ Ox.nonblockingMap = function(collection, iterator, that, callback, ms) { var last = Ox.last(arguments), type = Ox.typeOf(collection), results = type == 'object' ? {} : []; callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; that = arguments.length == 5 || ( arguments.length == 4 && Ox.isFunction(last) ) ? that : null; Ox.nonblockingForEach(collection, function(value, key, collection) { results[key] = iterator.call(that, value, key, collection); }, function() { callback(type == 'string' ? results.join('') : results); }, ms); }; /*@ Ox.parallelForEach `forEach` with asynchronous iterator, running in parallel (collection, iterator[, that], callback) -> undefined collection Collection iterator Iterator function value <*> Value key Key collection The collection callback Callback function that The iterator's this binding callback Callback function > Ox.parallelForEach(Ox.range(10), Ox.test.pfeIterator, function() { Ox.test(Ox.test.pfeNumber, 5); }) undefined @*/ Ox.parallelForEach = function(collection, iterator, that, callback) { var i = 0, n, type = Ox.typeOf(collection); callback = callback || (arguments.length == 3 ? arguments[2] : Ox.noop); collection = type == 'array' || type == 'object' ? collection : Ox.toArray(collection); n = Ox.len(collection); that = arguments.length == 4 ? that : null; Ox.forEach(collection, function(value, key, collection) { iterator.call(that, value, key, collection, function() { ++i == n && callback(); }); }); }; /*@ Ox.parallelMap Parallel `map` with asynchronous iterator (collection, iterator[, that], callback) -> undefined collection Collection iterator Iterator function value <*> Value key Key collection The collection callback Callback function that The iterator's this binding callback Callback function results Results > Ox.parallelMap(Ox.range(10), Ox.test.pmIterator, function(r) { Ox.test(Ox.sum(r), 0); }) undefined @*/ Ox.parallelMap = function() { asyncMap.apply(null, [Ox.parallelForEach].concat(Ox.toArray(arguments))); }; /*@ Ox.serialForEach `forEach` with asynchronous iterator, run serially (collection, iterator[, that], callback) -> undefined collection Collection iterator Iterator function value <*> Value key Key collection The collection callback Callback function that The iterator's this binding callback Callback function > Ox.serialForEach(Ox.range(10), Ox.test.sfeIterator, function() { Ox.test(Ox.test.sfeNumber, 5); }) undefined @*/ Ox.serialForEach = function(collection, iterator, that, callback) { var i = 0, keys, n, type = Ox.typeOf(collection); callback = callback || (arguments.length == 3 ? arguments[2] : Ox.noop); collection = type == 'array' || type == 'object' ? collection : Ox.toArray(collection); keys = type == 'object' ? Object.keys(collection) : Ox.range(collection.length); n = Ox.len(collection); that = arguments.length == 4 ? that : null; iterate(); function iterate(value) { if (value !== false) { if (keys[i] in collection) { iterator.call( that, collection[keys[i]], keys[i], collection, ++i < n ? iterate : callback ); } else { ++i < n ? iterate() : callback(); } } else { callback(); } } }; /*@ Ox.serialMap Serial `map` with asynchronous iterator (collection, iterator[, that], callback) -> undefined collection Collection iterator Iterator function value <*> Value key Key collection The collection callback Callback function that The iterator's this binding callback Callback function results Results > Ox.serialMap(Ox.range(10), Ox.test.smIterator, function(r) { Ox.test(Ox.sum(r), 0); }) undefined @*/ Ox.serialMap = function(collection, iterator, that, callback) { asyncMap.apply(null, [Ox.serialForEach].concat(Ox.toArray(arguments))); }; // FIXME: The above test with 10000 iterations blows the stack }());