diff --git a/source/Ox/js/Async.js b/source/Ox/js/Async.js new file mode 100644 index 00000000..84f43c5a --- /dev/null +++ b/source/Ox/js/Async.js @@ -0,0 +1,182 @@ +(function() { + + function asyncMap(forEach, col, iterator, that, callback) { + var type = Ox.typeOf(col), + results = type == 'object' ? {} : []; + callback = Ox.last(arguments); + that = arguments.length == 5 ? that : null; + forEach(col, function(val, key, obj, callback) { + iterator(val, key, obj, function(v) { + results[key] = v; + callback(); + }); + }, that, function() { + callback(type == 'string' ? results.join('') : results); + }); + }; + + Ox.asyncMap = function(arr, iterator, that, callback) { + callback = Ox.last(arguments); + that = arguments.length == 4 ? that : null; + if (arr.some(Ox.isArray)) { + Ox.serialMap(arr, function(val, key, obj, callback) { + Ox.parallelMap(Ox.makeArray(val), iterator, callback); + }, callback); + } else { + Ox.parallelMap(Ox.makeArray(val), iterator, callback); + } + }; + + /*@ + Ox.nonblockingForEach Non-blocking forEach + (col, iterator[, that], callback[, ms]) -> undefined + @*/ + Ox.nonblockingForEach = function(col, iterator, that, callback, ms) { + var i, keys, last = Ox.last(arguments), n, time = +new Date(); + callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; + col = type == 'array' || type == 'object' ? col : Ox.toArray(col); + keys = type == 'object' ? Object.keys(col) : Ox.range(col.length); + ms = ms || 1000; + n = Ox.len(col); + that = arguments.length == 5 || ( + arguments.length == 4 && Ox.isFunction(last) + ) ? that : null; + iterate(); + function iterate() { + keys[i] in col && iterator.call(that, col[keys[i]], keys[i], col, function() { + if (++i < n) { + if (+new Date() < time + ms) { + iterate(); + } else { + setTimeout(function() { + time += new Date(); + iterate(); + }); + } + } else { + callback(); + } + }); + } + }; + + Ox.nonblockingMap = function() { + asyncMap.apply(null, [Ox.nonblockingForEach].concat(Ox.toArray(arguments))); + }; + + /*@ + Ox.parallelForEach forEach with asynchronous iterator, running in parallel + (col, iterator[, that], callback) -> undefined + col Collection + iterator Iterator function + val <*> Value + key Key + obj The collection + callback Callback function + that The iterator's this binding + callback Callback function + @*/ + Ox.parallelForEach = function(col, iterator, that, callback) { + var i = 0, n, type = Ox.typeOf(col); + callback = Ox.last(arguments); + col = type == 'array' || type == 'object' ? col : Ox.toArray(col); + n = Ox.len(col); + that = arguments.length == 4 ? that : null; + Ox.forEach(col, function(val, key, obj) { + iterator.call(that, val, key, obj, function() { + ++i == n && callback(); + }); + }); + }; + + /*@ + Ox.parallelMap Parallel map with asynchronous iterator + (col, iterator[, that], callback) -> undefined + col Collection + iterator Iterator function + val <*> Value + key Key + obj The collection + callback Callback function + that The iterator's this binding + callback Callback function + results Results + + @*/ + Ox.parallelMap = function() { + asyncMap.apply(null, [Ox.parallelForEach].concat(Ox.toArray(arguments))); + }; + + /*@ + Ox.serialForEach forEach with asynchronous iterator, run serially + (col, iterator[, that], callback) -> undefined + col Collection + iterator Iterator function + val <*> Value + key Key + obj The collection + callback Callback function + that The iterator's this binding + callback Callback function + @*/ + Ox.serialForEach = function(col, iterator, that, callback) { + var i = 0, keys, n, type = Ox.typeOf(col); + callback = Ox.last(arguments); + col = type == 'array' || type == 'object' ? col : Ox.toArray(col); + keys = type == 'object' ? Object.keys(col) : Ox.range(col.length); + n = Ox.len(col); + that = arguments.length == 4 ? that : null; + iterate(); + function iterate() { + keys[i] in col && iterator.call(that, col[keys[i]], keys[i], col, function() { + ++i < n ? iterate() : callback(); + }); + } + }; + + /*@ + Ox.serialMap Serial map with asynchronous iterator + (col, iterator[, that], callback) -> undefined + col Collection + iterator Iterator function + val <*> Value + key Key + obj The collection + callback Callback function + that The iterator's this binding + callback Callback function + err Error object + results Results + + @*/ + Ox.serialMap = function(col, iterator, that, callback) { + asyncMap.apply(null, [Ox.serialForEach].concat(Ox.toArray(arguments))); + }; + +}());