diff --git a/README.md b/README.md index 97af2e4..8498814 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,15 @@ ## API -###prow.when(deferreds) -###prow.nextTick(task) +### prow.when(deferreds) +### prow.nextTick(task) ### prow.defer(timeout, timelimit) ### prow.waterfall(tasks) -### prow.parallel(tasks, maxThreads) -### prow.queue(tasks) -###prow.retry(task, times) -###prow.times(task, times) +### prow.parallel(tasks, maxThreads, managed) +### prow.queue(tasks, managed) +### prow.retry(task, times, delay) +### prow.times(task, times) +### prow.await(condition, checkDelay, timeLimit) ## License diff --git a/dist/prow.js b/dist/prow.js index bc77964..09877ca 100644 --- a/dist/prow.js +++ b/dist/prow.js @@ -1,142 +1,203 @@ (function() { - var a = {}; - a.when = function(b) { - if (b instanceof Promise && typeof b.then === "function") { - return b; + var prow = {}; + prow.when = function(deferreds) { + var deferred; + if (deferreds instanceof Promise && typeof deferreds.then === "function") { + if (deferreds instanceof Promise) { + return deferreds; + } else { + deferred = prow.defer(); + deferreds.then(function() { + deferred.resolve.apply(this, arguments); + }, function() { + deferred.reject.apply(this, arguments); + }); + return deferred.promise; + } } else { - var c = a.defer(); - c.resolve(b); - return c.promise; + return Promise.resolve(deferreds); } }; - a.nextTick = function(a) { + prow.nextTick = function(task) { if (process && process.nextTick) { - process.nextTick(a); + process.nextTick(task); } else { - setTimeout(a, 0); + setTimeout(task, 0); } }; - a.defer = function(a, b) { - var c = {}; - var d, e; - c.promise = new Promise(function(f, g) { - if (a) { - d = setTimeout(f, a); + prow.defer = function(timeout, timelimit) { + var defer = {}; + var timeoutResolve, timeoutReject; + defer.promise = new Promise(function(resolve, reject) { + if (timeout) { + timeoutResolve = setTimeout(resolve, timeout); } - if (b) { - e = setTimeout(g, b); + if (timelimit) { + timeoutReject = setTimeout(reject.bind(this, "PROW TIMEOUT"), timelimit); } - c.resolve = function() { - clearTimeout(d); - f.apply(this, arguments); + defer.resolve = function(result) { + clearTimeout(timeoutResolve); + resolve.apply(this, arguments); }; - c.reject = function() { - clearTimeout(e); - g.apply(this, arguments); + defer.reject = function(reason) { + clearTimeout(timeoutReject); + reject.apply(this, arguments); }; }); - return c; + return defer; }; - a.delay = function(a, b) { - var c = new Promise(function(c, d) { + prow.delay = function(timeout, result) { + var promise = new Promise(function(resolve, reject) { setTimeout(function() { - c(b); - }, a); + resolve(result); + }, timeout); }); - return c; + return promise; }; - a.limit = function(a, b) { - var c = new Promise(function(c, d) { + prow.limit = function(timelimit, reason) { + var promise = new Promise(function(resolve, reject) { setTimeout(function() { - d(b); - }, a); + reject(reason); + }, timelimit); }); - return c; + return promise; }; - a.waterfall = function(b) { - var c = b.length; - var d = a.defer(); + prow.waterfall = function(tasks) { + var length = tasks.length; + var deferred = prow.defer(); try { - var e = function(f, g) { - if (f >= c) { - d.resolve(g); + var process = function(cursor, result) { + if (cursor >= length) { + deferred.resolve(result); } else { - var h = b[f]; - a.when(h.call(null, g)).then(function(a) { - e(++f, a); - }, function(a) { - d.reject(a); - }).catch(function(a) { - d.reject(a); + var task = tasks[cursor]; + prow.when(task.call(null, result)).then(function(result) { + process(++cursor, result); + }, function(reason) { + deferred.reject(reason); + }).catch(function(err) { + deferred.reject(err); }); } }; - e(0); - } catch (f) { - d.reject(f); + process(0); + } catch (err) { + deferred.reject(err); } - return d.promise; + return deferred.promise; }; - a.parallel = function(b, c) { - var d = b.length; - var e = a.defer(); - c = Math.min(c || d, d); - var f = 0; - var g = 0; - var h = function() { - if (g >= d) { - if (f === 0) { - e.resolve(); + prow.parallel = function(tasks, maxThreads, managed) { + var length = tasks.length; + var deferred = prow.defer(); + maxThreads = Math.min(maxThreads || length, length); + var inProgress = 0; + var cursor = 0; + var process = function() { + if (cursor >= length) { + if (inProgress === 0) { + deferred.resolve(); } return; } - var i = b[g++]; - f++; - a.when(i.call()).then(function() { - f--; - h(); + var task = tasks[cursor++]; + inProgress++; + prow.when(task.call()).then(function() { + inProgress--; + process(); }, function() { - f--; - h(); + inProgress--; + process(); }).catch(function() { - f--; - h(); + inProgress--; + process(); }); - if (f < c) { - h(); + if (inProgress < maxThreads) { + process(); } }; - h(); - return e.promise; + process(); + if (!managed) { + return deferred.promise; + } else { + return { + push: function(newTasks) { + if (tasks) { + if (Array.isArray(newTasks)) { + tasks = tasks.concat(newTasks); + } else { + tasks.push(newTasks); + } + } + length = tasks.length; + }, + promise: deferred.promise + }; + } }; - a.queue = function(b) { - return a.parallel.call(this, b, 1); + prow.queue = function(tasks, managed) { + return prow.parallel.call(this, tasks, 1, managed); }; - a.retry = function(b, c) { - c = c === undefined ? 1 : c; - var d = a.defer(); - var e = function(a) { - if (c === 0) { - d.reject(a); + prow.retry = function(task, times, delay) { + times = times === undefined ? 1 : times; + var deferred = prow.defer(); + var rejHandler = function(reason) { + if (times === 0) { + deferred.reject(reason); } else { - f(--c); + if (delay !== undefined) { + prow.delay(delay).then(process.bind(this, --times)); + } else { + process(--times); + } } }; - var f = function(a) { - b.call().then(function(a) { - d.resolve(a); - }, e).catch(e); + var process = function(times) { + prow.when(task.call()).then(function(result) { + deferred.resolve(result); + }, rejHandler).catch(rejHandler); }; - f(--c); - return d.promise; + process(--times); + return deferred.promise; + }; + prow.times = function(task, times) { + times = times === undefined ? 1 : times; + var results = []; + var deferred = prow.defer(); + for (var i = 0; i < times; i++) { + results.push(task); + } + prow.queue(results).then(deferred.resolve.bind(deferred, results), deferred.resolve.bind(deferred, results)); + return deferred.promise; + }; + prow.await = function(condition, checkDelay, timeLimit) { + timeLimit = timeLimit || 0; + var rejected = false; + var timeoutId; + var deferred = prow.defer(null, timeLimit); + var check = function() { + var res = condition(); + if (res) { + deferred.resolve(res); + } else { + if (!rejected) { + timeoutId = setTimeout(check, checkDelay); + } + } + }; + deferred.promise.then(null, function() { + rejected = true; + clearTimeout(timeoutId); + }); + check(); + return deferred.promise; }; if (typeof module == "object" && module.exports) { - module.exports = a; + module.exports = prow; } else if (typeof define == "function" && define.amd) { define(function() { - return a; + return prow; }); } else if (typeof window == "object") { - window.prow = a; + window.prow = prow; } })(); \ No newline at end of file diff --git a/dist/prow.min.js b/dist/prow.min.js index 7813aca..d8227e3 100644 --- a/dist/prow.min.js +++ b/dist/prow.min.js @@ -1 +1 @@ -!function(){var a={};a.when=function(b){if(b instanceof Promise&&"function"==typeof b.then)return b;var c=a.defer();return c.resolve(b),c.promise},a.nextTick=function(a){process&&process.nextTick?process.nextTick(a):setTimeout(a,0)},a.defer=function(a,b){var c,d,e={};return e.promise=new Promise(function(f,g){a&&(c=setTimeout(f,a)),b&&(d=setTimeout(g,b)),e.resolve=function(){clearTimeout(c),f.apply(this,arguments)},e.reject=function(){clearTimeout(d),g.apply(this,arguments)}}),e},a.delay=function(a,b){var c=new Promise(function(c,d){setTimeout(function(){c(b)},a)});return c},a.limit=function(a,b){var c=new Promise(function(c,d){setTimeout(function(){d(b)},a)});return c},a.waterfall=function(b){var c=b.length,d=a.defer();try{var e=function(f,g){if(f>=c)d.resolve(g);else{var h=b[f];a.when(h.call(null,g)).then(function(a){e(++f,a)},function(a){d.reject(a)})["catch"](function(a){d.reject(a)})}};e(0)}catch(f){d.reject(f)}return d.promise},a.parallel=function(b,c){var d=b.length,e=a.defer();c=Math.min(c||d,d);var f=0,g=0,h=function(){if(g>=d)return void(0===f&&e.resolve());var i=b[g++];f++,a.when(i.call()).then(function(){f--,h()},function(){f--,h()})["catch"](function(){f--,h()}),c>f&&h()};return h(),e.promise},a.queue=function(b){return a.parallel.call(this,b,1)},a.retry=function(b,c){c=void 0===c?1:c;var d=a.defer(),e=function(a){0===c?d.reject(a):f(--c)},f=function(a){b.call().then(function(a){d.resolve(a)},e)["catch"](e)};return f(--c),d.promise},"object"==typeof module&&module.exports?module.exports=a:"function"==typeof define&&define.amd?define(function(){return a}):"object"==typeof window&&(window.prow=a)}(); \ No newline at end of file +!function(){var a={};a.when=function(b){var c;return b instanceof Promise&&"function"==typeof b.then?b instanceof Promise?b:(c=a.defer(),b.then(function(){c.resolve.apply(this,arguments)},function(){c.reject.apply(this,arguments)}),c.promise):Promise.resolve(b)},a.nextTick=function(a){process&&process.nextTick?process.nextTick(a):setTimeout(a,0)},a.defer=function(a,b){var c,d,e={};return e.promise=new Promise(function(f,g){a&&(c=setTimeout(f,a)),b&&(d=setTimeout(g.bind(this,"PROW TIMEOUT"),b)),e.resolve=function(a){clearTimeout(c),f.apply(this,arguments)},e.reject=function(a){clearTimeout(d),g.apply(this,arguments)}}),e},a.delay=function(a,b){var c=new Promise(function(c,d){setTimeout(function(){c(b)},a)});return c},a.limit=function(a,b){var c=new Promise(function(c,d){setTimeout(function(){d(b)},a)});return c},a.waterfall=function(b){var c=b.length,d=a.defer();try{var e=function(f,g){if(f>=c)d.resolve(g);else{var h=b[f];a.when(h.call(null,g)).then(function(a){e(++f,a)},function(a){d.reject(a)})["catch"](function(a){d.reject(a)})}};e(0)}catch(f){d.reject(f)}return d.promise},a.parallel=function(b,c,d){var e=b.length,f=a.defer();c=Math.min(c||e,e);var g=0,h=0,i=function(){if(h>=e)return void(0===g&&f.resolve());var d=b[h++];g++,a.when(d.call()).then(function(){g--,i()},function(){g--,i()})["catch"](function(){g--,i()}),c>g&&i()};return i(),d?{push:function(a){b&&(Array.isArray(a)?b=b.concat(a):b.push(a)),e=b.length},promise:f.promise}:f.promise},a.queue=function(b,c){return a.parallel.call(this,b,1,c)},a.retry=function(b,c,d){c=void 0===c?1:c;var e=a.defer(),f=function(b){0===c?e.reject(b):void 0!==d?a.delay(d).then(g.bind(this,--c)):g(--c)},g=function(c){a.when(b.call()).then(function(a){e.resolve(a)},f)["catch"](f)};return g(--c),e.promise},a.times=function(b,c){c=void 0===c?1:c;for(var d=[],e=a.defer(),f=0;c>f;f++)d.push(b);return a.queue(d).then(e.resolve.bind(e,d),e.resolve.bind(e,d)),e.promise},a.await=function(b,c,d){d=d||0;var e,f=!1,g=a.defer(null,d),h=function(){var a=b();a?g.resolve(a):f||(e=setTimeout(h,c))};return g.promise.then(null,function(){f=!0,clearTimeout(e)}),h(),g.promise},"object"==typeof module&&module.exports?module.exports=a:"function"==typeof define&&define.amd?define(function(){return a}):"object"==typeof window&&(window.prow=a)}(); \ No newline at end of file diff --git a/gruntfile.js b/gruntfile.js index 275035d..d6b7827 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -17,6 +17,7 @@ module.exports = function (grunt) { }, js: { options: { + mangle: false, compress: false, beautify: true }, @@ -58,5 +59,7 @@ module.exports = function (grunt) { grunt.registerTask('default', ['jshint', 'uglify', 'simplemocha']); + grunt.registerTask('test', ['simplemocha']); + grunt.registerTask('w', ['default', 'watch']); }; \ No newline at end of file diff --git a/package.json b/package.json index 29e9806..6d49e39 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "name": "prow", - "version": "0.0.1", + "version": "0.2.2", "description": "JS Promises Flow Lib", "main": "dist/prow.js", "scripts": { + "prepublish" : "grunt", "test": "grunt test" }, "repository": { @@ -11,6 +12,7 @@ "url": "https://github.com/rzcoder/prow.git" }, "keywords": [ + "js", "promise", "flow" ], diff --git a/src/prow.js b/src/prow.js index 4f89b3e..3c73174 100644 --- a/src/prow.js +++ b/src/prow.js @@ -7,12 +7,21 @@ * @returns {Promise} */ prow.when = function (deferreds) { - if (deferreds instanceof Promise && typeof result.then === "function") { - return result; + var deferred; + if (deferreds instanceof Promise && typeof deferreds.then === "function") { + if (deferreds instanceof Promise) { + return deferreds; + } else { + deferred = prow.defer(); + deferreds.then(function () { + deferred.resolve.apply(this, arguments); + }, function () { + deferred.reject.apply(this, arguments); + }); + return deferred.promise; + } } else { - var deferred = prow.defer(); - deferred.resolve(deferreds); - return deferred.promise; + return Promise.resolve(deferreds); } }; @@ -39,21 +48,20 @@ timeoutResolve = setTimeout(resolve, timeout); } if (timelimit) { - timeoutReject = setTimeout(reject, timelimit); + timeoutReject = setTimeout(reject.bind(this, 'PROW TIMEOUT'), timelimit); } - defer.resolve = function () { + defer.resolve = function (result) { clearTimeout(timeoutResolve); resolve.apply(this, arguments); }; - defer.reject = function () { + defer.reject = function (reason) { clearTimeout(timeoutReject); reject.apply(this, arguments); }; }); - return defer; }; @@ -126,9 +134,9 @@ * Run the tasks in parallel, without waiting until the previous function has completed. No results passed from promise to promise. * @param tasks {Array} Array of functions which returns promises * @param maxThreads {int} The maximum number of tasks to run at any time. Default: tasks.length - * @returns {Promise} Promise which will resolve after all tasks done (resolved o rejected). + * @returns {Promise|Que Control Api} Promise which will resolve after all tasks done (resolved o rejected) OR Object for controlling tasks que */ - prow.parallel = function (tasks, maxThreads) { + prow.parallel = function (tasks, maxThreads, managed) { var length = tasks.length; var deferred = prow.defer(); maxThreads = Math.min(maxThreads || length, length); @@ -164,7 +172,25 @@ process(); - return deferred.promise; + if (!managed) { + return deferred.promise; + } else { + return { + push: function(newTasks) { + if (tasks) { + if (Array.isArray(newTasks)) { + tasks = tasks.concat(newTasks); + } else { + tasks.push(newTasks); + } + } + + length = tasks.length; + }, + + promise: deferred.promise + }; + } }; /** @@ -172,29 +198,34 @@ * @param tasks {Array} Array of functions which returns promises * @returns {Promise} Promise which will resolve after all tasks done (resolved o rejected). */ - prow.queue = function (tasks) { - return prow.parallel.call(this, tasks, 1); + prow.queue = function (tasks, managed) { + return prow.parallel.call(this, tasks, 1, managed); }; /** * Attempts to get a successful response from `task` no more than `times` times before returning an error. * @param task {function} Function which return promise * @param times {int} Number of try times, before reject + * @param delay {int} Delay in ms between tries * @returns {Promise} Promise which resolve on first successful try, or reject after defined tries */ - prow.retry = function (task, times) { + prow.retry = function (task, times, delay) { times = times === undefined ? 1 : times; var deferred = prow.defer(); var rejHandler = function (reason) { if (times === 0) { deferred.reject(reason); } else { - process(--times); + if (delay !== undefined) { + prow.delay(delay).then(process.bind(this, --times)); + } else { + process(--times); + } } }; var process = function (times) { - task.call().then(function (result) { + prow.when(task.call()).then(function (result) { deferred.resolve(result); }, rejHandler).catch(rejHandler); }; @@ -214,9 +245,40 @@ var results = []; var deferred = prow.defer(); for (var i = 0; i < times; i++) { - results.push(task.call()); + results.push(task); } - Promise.all(results).then(deferred.resolve.bind(deferred, results), deferred.resolve.bind(deferred, results)); + prow.queue(results).then(deferred.resolve.bind(deferred, results), deferred.resolve.bind(deferred, results)); + return deferred.promise; + }; + + /** + * Awaiting while condition function not return positive bool value. + * @param condition {fucntion} + * @param checkDelay {int} Delay in ms between checks + * @param timeLimit {int} Max time awaiting (0 for infinity) + * @returns {Promise} + */ + prow.await = function (condition, checkDelay, timeLimit) { + timeLimit = timeLimit || 0; + + var rejected = false; + var timeoutId; + var deferred = prow.defer(null, timeLimit); + var check = function() { + var res = condition(); + if (res) { + deferred.resolve(res); + } else { + if (!rejected) { + timeoutId = setTimeout(check, checkDelay); + } + } + }; + deferred.promise.then(null, function() { + rejected = true; + clearTimeout(timeoutId); + }); + check(); return deferred.promise; };