From 3b51d32e8ae813302e14d926786738079fed6331 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Sun, 30 Oct 2016 22:48:31 +0500 Subject: [PATCH 01/14] typescript --- .gitignore | 5 +- .npmignore | 8 ++ README.md | 31 +---- dist/prow.js | 203 -------------------------------- dist/prow.min.js | 1 - gruntfile.js | 65 ----------- package.json | 34 +++--- src/prow.js | 297 ----------------------------------------------- tests/defer.js | 15 --- tests/delay.js | 19 --- tsconfig.json | 21 ++++ tslint.json | 73 ++++++++++++ 12 files changed, 123 insertions(+), 649 deletions(-) create mode 100644 .npmignore delete mode 100644 dist/prow.js delete mode 100644 dist/prow.min.js delete mode 100644 gruntfile.js delete mode 100644 src/prow.js delete mode 100644 tests/defer.js delete mode 100644 tests/delay.js create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.gitignore b/.gitignore index 35a8b70..df579e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .DS_Store .idea .tmp -tmp -node_modules -*.log \ No newline at end of file +todo.md +node_modules \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..2f5102b --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +test +todo.md +node_modules +src +.tmp +.travis.yml +tsconfig.json +tslint.json \ No newline at end of file diff --git a/README.md b/README.md index 8498814..257e94c 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,5 @@ -# JS Promises Flow Control +# Steersman -## Install -``` - npm install prow -``` - -## API - -### prow.when(deferreds) -### prow.nextTick(task) -### prow.defer(timeout, timelimit) -### prow.waterfall(tasks) -### prow.parallel(tasks, maxThreads, managed) -### prow.queue(tasks, managed) -### prow.retry(task, times, delay) -### prow.times(task, times) -### prow.await(condition, checkDelay, timeLimit) - -## License - -Copyright (c) 2015 rzcoder - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +_Lightweight JS router for browsers & node_ + +Work in progress \ No newline at end of file diff --git a/dist/prow.js b/dist/prow.js deleted file mode 100644 index b374c42..0000000 --- a/dist/prow.js +++ /dev/null @@ -1,203 +0,0 @@ -(function() { - 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 { - return Promise.resolve(deferreds); - } - }; - prow.nextTick = function(task) { - if (process && process.nextTick) { - process.nextTick(task); - } else { - setTimeout(task, 0); - } - }; - prow.defer = function(timeout, timelimit) { - var defer = {}; - var timeoutResolve, timeoutReject; - defer.promise = new Promise(function(resolve, reject) { - if (timeout) { - timeoutResolve = setTimeout(resolve, timeout); - } - if (timelimit) { - timeoutReject = setTimeout(reject.bind(this, "PROW TIMEOUT"), timelimit); - } - defer.resolve = function() { - clearTimeout(timeoutResolve); - resolve.apply(this, arguments); - }; - defer.reject = function() { - clearTimeout(timeoutReject); - reject.apply(this); - }; - }); - return defer; - }; - prow.delay = function(timeout, result) { - var promise = new Promise(function(resolve, reject) { - setTimeout(function() { - resolve(result); - }, timeout); - }); - return promise; - }; - prow.limit = function(timelimit, reason) { - var promise = new Promise(function(resolve, reject) { - setTimeout(function() { - reject(reason); - }, timelimit); - }); - return promise; - }; - prow.waterfall = function(tasks) { - var length = tasks.length; - var deferred = prow.defer(); - try { - var process = function(cursor, result) { - if (cursor >= length) { - deferred.resolve(result); - } else { - 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); - }); - } - }; - process(0); - } catch (err) { - deferred.reject(err); - } - return deferred.promise; - }; - 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 task = tasks[cursor++]; - inProgress++; - prow.when(task.call()).then(function() { - inProgress--; - process(); - }, function() { - inProgress--; - process(); - }).catch(function() { - inProgress--; - process(); - }); - if (inProgress < maxThreads) { - process(); - } - }; - 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 - }; - } - }; - prow.queue = function(tasks, managed) { - return prow.parallel.call(this, tasks, 1, managed); - }; - 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 { - if (delay !== undefined) { - prow.delay(delay).then(process.bind(this, --times)); - } else { - process(--times); - } - } - }; - var process = function(times) { - prow.when(task.call()).then(function(result) { - deferred.resolve(result); - }, rejHandler).catch(rejHandler); - }; - 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 = prow; - } else if (typeof define == "function" && define.amd) { - define(function() { - return prow; - }); - } else if (typeof window == "object") { - window.prow = prow; - } -})(); \ No newline at end of file diff --git a/dist/prow.min.js b/dist/prow.min.js deleted file mode 100644 index bf801be..0000000 --- a/dist/prow.min.js +++ /dev/null @@ -1 +0,0 @@ -!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(){clearTimeout(c),f.apply(this,arguments)},e.reject=function(){clearTimeout(d),g.apply(this)}}),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 deleted file mode 100644 index d6b7827..0000000 --- a/gruntfile.js +++ /dev/null @@ -1,65 +0,0 @@ -module.exports = function (grunt) { - var config = { - pkg: require('./package.json'), - isDev: grunt.option('no-dev') - }; - - grunt.initConfig({ - clean: { - dist: ['dist/'] - }, - - uglify: { - js_min: { - files: { - 'dist/prow.min.js': ['src/prow.js'] - } - }, - js: { - options: { - mangle: false, - compress: false, - beautify: true - }, - files: { - 'dist/prow.js': ['src/prow.js'] - } - } - }, - - jshint: { - js: { - files: { - src: ['src/**/*.js', 'tests/**/*.js'] - } - } - }, - - simplemocha: { - options: { - reporter: 'list' - }, - all: {src: ['test/**/*.js']} - }, - - watch: { - js: { - files: ['src/**/*.js', 'tests/**/*.js'], - tasks: ['jshint'] - } - } - }); - - require('time-grunt')(grunt); - require('jit-grunt')(grunt, { - 'simplemocha': 'grunt-simple-mocha' - }); - - grunt.registerTask('compile', ['uglify']); - - 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 f85ef8c..8a7d886 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,31 @@ { "name": "prow", - "version": "0.2.1", + "version": "1.0.0", "description": "JS Promises Flow Lib", - "main": "dist/prow.js", - "scripts": { - "prepublish" : "grunt", - "test": "grunt test" - }, + "main": "lib/prow.js", + "typings": "lib/prow.d.ts", + "author": "rzcoder", + "license": "MIT", "repository": { "type": "git", "url": "https://github.com/rzcoder/prow.git" }, + "bugs": { + "url": "https://github.com/rzcoder/prow/issues" + }, "keywords": [ "js", "promise", "flow" ], - "author": "rzcoder@gmail.com", - "license": "MIT", - "bugs": { - "url": "https://github.com/rzcoder/prow/issues" + "scripts": { + "build": "tsc", + "test": "mocha", + "-pretest": "tslint --project ./" }, - "homepage": "https://github.com/rzcoder/prow", "devDependencies": { - "chai": "^3.0.0", - "grunt": "^0.4.5", - "grunt-contrib-jshint": "^0.11.2", - "grunt-contrib-uglify": "^0.9.1", - "grunt-simple-mocha": "^0.4.0", - "jit-grunt": "^0.9.1", - "sinon": "^1.14.1", - "time-grunt": "^1.2.1" + "chai": "^3.5.0", + "chai-as-promised": "^6.0.0", + "mocha": "^3.1.2" } } diff --git a/src/prow.js b/src/prow.js deleted file mode 100644 index 098b4dc..0000000 --- a/src/prow.js +++ /dev/null @@ -1,297 +0,0 @@ -(function () { - var prow = {}; - - /** - * Return Promise for any data - * @param deferreds {Promise|*} - * @returns {Promise} - */ - 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 { - return Promise.resolve(deferreds); - } - }; - - prow.nextTick = function (task) { - if (process && process.nextTick) { - process.nextTick(task); - } else { - setTimeout(task, 0); - } - }; - - /** - * Create deferred object - * @param timeout {int} Timeout in ms. If specified deferred will call resolve after defined time - * @param timelimit {int} Timeout in ms. If specified deferred will call reject after defined time - * @returns {Defer} Defer object - */ - prow.defer = function (timeout, timelimit) { - var defer = {}; - var timeoutResolve, timeoutReject; - - defer.promise = new Promise(function (resolve, reject) { - if (timeout) { - timeoutResolve = setTimeout(resolve, timeout); - } - if (timelimit) { - timeoutReject = setTimeout(reject.bind(this, 'PROW TIMEOUT'), timelimit); - } - - defer.resolve = function () { - clearTimeout(timeoutResolve); - resolve.apply(this, arguments); - }; - - defer.reject = function () { - clearTimeout(timeoutReject); - reject.apply(this); - }; - }); - - return defer; - }; - - /** - * Promise which auto resolve after timeout - * @param timeout {int} Timeout in ms - * @param result {*} Result to provide in promise resolve - * @returns {Promise} Promise object - */ - prow.delay = function (timeout, result) { - var promise = new Promise(function (resolve, reject) { - setTimeout(function () { - resolve(result); - }, timeout); - }); - - return promise; - }; - - /** - * Promise which auto reject after timelimit - * @param timelimit {int} Timeout in ms - * @param reason {*} Result to provide in promise reject - * @returns {Promise} Promise object - */ - prow.limit = function (timelimit, reason) { - var promise = new Promise(function (resolve, reject) { - setTimeout(function () { - reject(reason); - }, timelimit); - }); - - return promise; - }; - - /** - * Runs the tasks array of functions in series, each passing their results to the next in the array. - * @param tasks {Array} Array of functions which returns promises - * @returns {Promise} Promise object - */ - prow.waterfall = function (tasks) { - var length = tasks.length; - var deferred = prow.defer(); - - try { - var process = function (cursor, result) { - if (cursor >= length) { - deferred.resolve(result); - } else { - 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); - }); - } - }; - - process(0); - } catch (err) { - deferred.reject(err); - } - return deferred.promise; - }; - - /** - * 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|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, 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 task = tasks[cursor++]; - inProgress++; - prow.when(task.call()).then(function () { - inProgress--; - process(); - }, function () { - inProgress--; - process(); - }).catch(function () { - inProgress--; - process(); - }); - - if (inProgress < maxThreads) { - process(); - } - }; - - 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 - }; - } - }; - - /** - * Run the tasks one by one. No results passed from promise to promise. - * @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, 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, delay) { - times = times === undefined ? 1 : times; - var deferred = prow.defer(); - var rejHandler = function (reason) { - if (times === 0) { - deferred.reject(reason); - } else { - if (delay !== undefined) { - prow.delay(delay).then(process.bind(this, --times)); - } else { - process(--times); - } - } - }; - - var process = function (times) { - prow.when(task.call()).then(function (result) { - deferred.resolve(result); - }, rejHandler).catch(rejHandler); - }; - - process(--times); - return deferred.promise; - }; - - /** - * Calls the `task` function n times, return promise which will resolve with array of promises for each task call - * @param task {function} Function which return promise - * @param times {int} Number of call times - * @returns {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; - }; - - /** - * 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; - }; - - /** - * Module loaders - */ - if (typeof module == 'object' && module.exports) { - module.exports = prow; - } else if (typeof define == 'function' && define.amd) { - define(function () { - return prow; - }); - } else if (typeof window == 'object') { - window.prow = prow; - } -})(); \ No newline at end of file diff --git a/tests/defer.js b/tests/defer.js deleted file mode 100644 index 3d4aa32..0000000 --- a/tests/defer.js +++ /dev/null @@ -1,15 +0,0 @@ -var assert = require("chai").assert; -var prow = require("../dist/prow"); - -describe("Prow Defer", function () { - it("should create defer object", function () { - assert.isFunction(prow.defer, 'Prow should have defer method'); - - var deferred = prow.defer(); - - assert.isFunction(deferred.resolve, 'deferred should have resolve method'); - assert.isFunction(deferred.reject, 'deferred should have reject method'); - assert.equal(deferred.promise.constructor.name, 'Promise', 'deferred should have promise object'); - assert.isFunction(deferred.promise.then, 'deferred should have promise object'); - }); -}); \ No newline at end of file diff --git a/tests/delay.js b/tests/delay.js deleted file mode 100644 index cd9d535..0000000 --- a/tests/delay.js +++ /dev/null @@ -1,19 +0,0 @@ -var assert = require("chai").assert; -var sinon = require('sinon'); -var prow = require("../dist/prow"); - -describe("Prow Delay", function () { - beforeEach(function() { - this.sinon = sinon.sandbox.create(); - }); - - it("should create delay promise", function () { - assert.isFunction(prow.delay, 'Prow should have delay method'); - - var promise = prow.delay(5); - }); - - afterEach(function(){ - this.sinon.restore(); - }); -}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..556dc9e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es5", + "outDir": "lib", + "sourceMap": false, + "declaration": true, + "strictNullChecks": false, + "module": "commonjs", + "removeComments": false, + "lib": [ + "es5", + "dom", + "es2015.promise" + ] + }, + "exclude": [ + "node_modules", + "test", + "lib" + ] +} \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..ec91139 --- /dev/null +++ b/tslint.json @@ -0,0 +1,73 @@ +{ + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": false, + "interface-name": true, + "jsdoc-format": true, + "no-consecutive-blank-lines" : true, + "no-debugger": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": true, + "no-shadowed-variable": true, + "no-switch-case-fall-through": true, + "no-unreachable": true, + "no-unused-expression": true, + "no_unused-variable": [ + true, + "check-parameters" + ], + "no-use-before-declare": false, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace", + "check-catch" + ], + "quotemark": [ + true, + "double" + ], + "semicolon": true, + "trailing-comma": [ + true, + { + "multiline": "never", + "singleline": "never" + } + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file From dffe908e6731ae87b7e75793ff781e09bf42648f Mon Sep 17 00:00:00 2001 From: rzcoder Date: Mon, 31 Oct 2016 01:54:00 +0500 Subject: [PATCH 02/14] waterfall --- README.md | 31 ++++++++++++-- package.json | 7 +++- src/prow.ts | 17 ++++++++ src/types.ts | 4 ++ test/waterfall.js | 104 ++++++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 1 - 6 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 src/prow.ts create mode 100644 src/types.ts create mode 100644 test/waterfall.js diff --git a/README.md b/README.md index 257e94c..8498814 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,28 @@ -# Steersman +# JS Promises Flow Control -_Lightweight JS router for browsers & node_ - -Work in progress \ No newline at end of file +## Install +``` + npm install prow +``` + +## API + +### prow.when(deferreds) +### prow.nextTick(task) +### prow.defer(timeout, timelimit) +### prow.waterfall(tasks) +### prow.parallel(tasks, maxThreads, managed) +### prow.queue(tasks, managed) +### prow.retry(task, times, delay) +### prow.times(task, times) +### prow.await(condition, checkDelay, timeLimit) + +## License + +Copyright (c) 2015 rzcoder + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/package.json b/package.json index 8a7d886..ae2dd24 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,14 @@ "scripts": { "build": "tsc", "test": "mocha", - "-pretest": "tslint --project ./" + "lint": "tslint --project ./" }, "devDependencies": { "chai": "^3.5.0", "chai-as-promised": "^6.0.0", - "mocha": "^3.1.2" + "lodash": "^4.16.4", + "mocha": "^3.1.2", + "tslint": "^3.15.1", + "typescript": "^2.0.6" } } diff --git a/src/prow.ts b/src/prow.ts new file mode 100644 index 0000000..8411775 --- /dev/null +++ b/src/prow.ts @@ -0,0 +1,17 @@ +import { + Task, Tasks +} from "./types"; + +export function waterfall(tasks: Tasks): Promise { + if (tasks.length === 0) { + return Promise.resolve(); + } + + let promise = tasks[0](); + if (tasks.length > 1) { + for (const task of tasks) { + promise = promise.then(task); + } + } + return promise; +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..609d12c --- /dev/null +++ b/src/types.ts @@ -0,0 +1,4 @@ +export interface Task { + (...args: any[]): Promise +} +export type Tasks = Task[]; \ No newline at end of file diff --git a/test/waterfall.js b/test/waterfall.js new file mode 100644 index 0000000..a61d589 --- /dev/null +++ b/test/waterfall.js @@ -0,0 +1,104 @@ +const _ = require("lodash"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +chai.use(chaiAsPromised); +const {assert, expect} = chai; +const prow = require("../lib/prow"); + + +function resolvePromise(value) { + return function (data) { + if (data) { + return Promise.resolve(data + 1); + } + return Promise.resolve(value); + } +} + +function rejectPromise(value) { + return function () { + return Promise.reject(value); + } +} + +describe("Waterfall", function () { + it("single resolved promise", function () { + return assert.becomes(prow.waterfall([resolvePromise("resolved value")]), "resolved value"); + }); + + it("single rejected promise", function () { + return assert.isRejected(prow.waterfall([rejectPromise("rejected reason")]), "rejected reason"); + }); + + it("few resolved promises", function () { + return assert.becomes(prow.waterfall([resolvePromise(1), resolvePromise(), resolvePromise(), resolvePromise()]), 5); + }); + + it("few resolved promises with rejected one", function () { + return assert.isRejected( + prow.waterfall([resolvePromise(), resolvePromise(), rejectPromise("rejected reason"), resolvePromise()]), + "rejected reason" + ); + }); + + it("data waterfall", function () { + return assert.becomes( + prow.waterfall([ + function () { + return Promise.resolve("first"); + }, + function (data) { + return Promise.resolve({ + [data]: 42 + }); + }, + function (data) { + data["first"] *= 10; + return Promise.resolve(data); + }, + function (data) { + return Promise.resolve(data).then(_.toPairs); + }, + ]), + [["first", 420]] + ); + }); + + it("combine waterfalls", function () { + const promise = prow.waterfall([ + function () { + return Promise.resolve("first"); + }, + function (data) { + return [ + function () { + return Promise.resolve({ + [data]: 42 + }); + }, + function (data) { + return Promise.resolve(data.first + 10); + } + ] + } + ]).then(prow.waterfall).then((data) => { + return prow.waterfall([ + function () { + return Promise.resolve({ + [data]: "second" + }); + }, + function (data) { + data["third"] = data[52] + "_"; + return Promise.resolve(data); + } + ]); + }); + + return assert.becomes(promise, { + "52": "second", + "third": "second_" + }); + }); + +}); diff --git a/tsconfig.json b/tsconfig.json index 556dc9e..b7a6bb5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,6 @@ "removeComments": false, "lib": [ "es5", - "dom", "es2015.promise" ] }, From ed9f6f5740e7c4a11b841397b5913e469f9ca249 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Tue, 1 Nov 2016 02:09:33 +0500 Subject: [PATCH 03/14] wip --- src/functions/delay.ts | 5 +++++ src/functions/parallel.ts | 15 +++++++++++++++ src/functions/retry.ts | 23 +++++++++++++++++++++++ src/functions/timeout.ts | 16 ++++++++++++++++ src/functions/times.ts | 11 +++++++++++ src/functions/waterfall.ts | 17 +++++++++++++++++ src/prow.ts | 32 +++++++++++++++++--------------- src/types.ts | 5 ++++- tsconfig.json | 1 + 9 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 src/functions/delay.ts create mode 100644 src/functions/parallel.ts create mode 100644 src/functions/retry.ts create mode 100644 src/functions/timeout.ts create mode 100644 src/functions/times.ts create mode 100644 src/functions/waterfall.ts diff --git a/src/functions/delay.ts b/src/functions/delay.ts new file mode 100644 index 0000000..3c68217 --- /dev/null +++ b/src/functions/delay.ts @@ -0,0 +1,5 @@ +export function delay(time: number, value?: any): Promise { + return new Promise(function (resolve) { + setTimeout(resolve.bind(null, value), time); + }); +} \ No newline at end of file diff --git a/src/functions/parallel.ts b/src/functions/parallel.ts new file mode 100644 index 0000000..3217020 --- /dev/null +++ b/src/functions/parallel.ts @@ -0,0 +1,15 @@ +import { + Tasks +} from "../types"; + + +export function parallel(tasks: Tasks, maxThreads?: number): Promise { + if (!maxThreads) { + maxThreads = tasks.length; + } + + return new Promise(function (resolve, reject) { + + + }); +} diff --git a/src/functions/retry.ts b/src/functions/retry.ts new file mode 100644 index 0000000..746dd57 --- /dev/null +++ b/src/functions/retry.ts @@ -0,0 +1,23 @@ +import { + Task +} from "../types"; + +function process(task: Task, times: number, reasons: any[], resolve, reject) { + task().then((result) => { + resolve(result); + }).catch((reason) => { + reasons.push(reason); + if (reasons.length >= times) { + reject(reasons); + } else { + process(task, times, resolve, reject, reasons); + } + }); +} + +export function retry(task: Task, times: number): Promise { + return new Promise(function (resolve, reject) { + const reasons = []; + process(task, times, reasons, resolve, reject); + }); +} diff --git a/src/functions/timeout.ts b/src/functions/timeout.ts new file mode 100644 index 0000000..96911ab --- /dev/null +++ b/src/functions/timeout.ts @@ -0,0 +1,16 @@ +import { + Task, TimeoutError +} from "../types"; + +export function timeout(time: number, task: Task): Promise { + return new Promise(function (resolve, reject) { + const timeout = setTimeout(reject.bind(null, new TimeoutError()), time); + task().then((result) => { + resolve(result); + clearTimeout(timeout); + }, (reason) => { + reject(reason); + clearTimeout(timeout); + }); + }); +} \ No newline at end of file diff --git a/src/functions/times.ts b/src/functions/times.ts new file mode 100644 index 0000000..1665594 --- /dev/null +++ b/src/functions/times.ts @@ -0,0 +1,11 @@ +import { + Task +} from "../types"; + + +export function times(task: Task, times: number, stopOnFirstReject: boolean): Promise { + return new Promise(function (resolve, reject) { + const results = []; + + }); +} diff --git a/src/functions/waterfall.ts b/src/functions/waterfall.ts new file mode 100644 index 0000000..20246fe --- /dev/null +++ b/src/functions/waterfall.ts @@ -0,0 +1,17 @@ +import { + Tasks +} from "../types"; + +export function waterfall(tasks: Tasks): Promise { + if (tasks.length === 0) { + return Promise.resolve(); + } + + let promise = tasks[0](); + if (tasks.length > 1) { + for (const task of tasks) { + promise = promise.then(task); + } + } + return promise; +} diff --git a/src/prow.ts b/src/prow.ts index 8411775..6935109 100644 --- a/src/prow.ts +++ b/src/prow.ts @@ -1,17 +1,19 @@ -import { - Task, Tasks +export { + Task, Tasks, TimeoutError } from "./types"; -export function waterfall(tasks: Tasks): Promise { - if (tasks.length === 0) { - return Promise.resolve(); - } - - let promise = tasks[0](); - if (tasks.length > 1) { - for (const task of tasks) { - promise = promise.then(task); - } - } - return promise; -} \ No newline at end of file +export {delay} from "./functions/delay"; +export {timeout} from "./functions/timeout"; +export {waterfall} from "./functions/waterfall"; +export {retry} from "./functions/retry"; + + + + + + + + + + + diff --git a/src/types.ts b/src/types.ts index 609d12c..6c60853 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,7 @@ export interface Task { (...args: any[]): Promise } -export type Tasks = Task[]; \ No newline at end of file +export type Tasks = Task[]; + +export class TimeoutError extends Error{ +} diff --git a/tsconfig.json b/tsconfig.json index b7a6bb5..2299450 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "module": "commonjs", "removeComments": false, "lib": [ + "dom", "es5", "es2015.promise" ] From 90d93316eca6c3fd0eaee91f67a8cc2ac087bc91 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Tue, 1 Nov 2016 23:44:48 +0500 Subject: [PATCH 04/14] wip --- .travis.yml | 13 ++++++++ lib/functions/delay.d.ts | 1 + lib/functions/delay.js | 7 ++++ lib/functions/links.d.ts | 3 ++ lib/functions/links.js | 9 +++++ lib/functions/parallel.d.ts | 0 lib/functions/parallel.js | 13 ++++++++ lib/functions/retry.d.ts | 2 ++ lib/functions/retry.js | 21 ++++++++++++ lib/functions/timeout.d.ts | 2 ++ lib/functions/timeout.js | 15 +++++++++ lib/functions/times.d.ts | 2 ++ lib/functions/times.js | 33 ++++++++++++++++++ lib/functions/waterfall.d.ts | 2 ++ lib/functions/waterfall.js | 15 +++++++++ lib/prow.d.ts | 6 ++++ lib/prow.js | 13 ++++++++ lib/types.d.ts | 6 ++++ lib/types.js | 14 ++++++++ package.json | 4 ++- src/functions/parallel.ts | 12 +++---- src/functions/retry.ts | 8 ++--- src/functions/timeout.ts | 4 +-- src/functions/times.ts | 34 ++++++++++++++++--- src/prow.ts | 14 ++------ src/types.ts | 9 ++--- test/delay.js | 28 ++++++++++++++++ test/retry.js | 41 +++++++++++++++++++++++ test/timeout.js | 34 +++++++++++++++++++ test/times.js | 65 ++++++++++++++++++++++++++++++++++++ 30 files changed, 396 insertions(+), 34 deletions(-) create mode 100644 .travis.yml create mode 100644 lib/functions/delay.d.ts create mode 100644 lib/functions/delay.js create mode 100644 lib/functions/links.d.ts create mode 100644 lib/functions/links.js create mode 100644 lib/functions/parallel.d.ts create mode 100644 lib/functions/parallel.js create mode 100644 lib/functions/retry.d.ts create mode 100644 lib/functions/retry.js create mode 100644 lib/functions/timeout.d.ts create mode 100644 lib/functions/timeout.js create mode 100644 lib/functions/times.d.ts create mode 100644 lib/functions/times.js create mode 100644 lib/functions/waterfall.d.ts create mode 100644 lib/functions/waterfall.js create mode 100644 lib/prow.d.ts create mode 100644 lib/prow.js create mode 100644 lib/types.d.ts create mode 100644 lib/types.js create mode 100644 test/delay.js create mode 100644 test/retry.js create mode 100644 test/timeout.js create mode 100644 test/times.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dbdaa9a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - '4.0' + - 'stable' + +sudo: false + +before_install: + - npm install -g npm@latest + - npm install -g grunt-cli + +install: + - npm install \ No newline at end of file diff --git a/lib/functions/delay.d.ts b/lib/functions/delay.d.ts new file mode 100644 index 0000000..23b81bd --- /dev/null +++ b/lib/functions/delay.d.ts @@ -0,0 +1 @@ +export declare function delay(time: number, value?: any): Promise; diff --git a/lib/functions/delay.js b/lib/functions/delay.js new file mode 100644 index 0000000..57e1fa5 --- /dev/null +++ b/lib/functions/delay.js @@ -0,0 +1,7 @@ +"use strict"; +function delay(time, value) { + return new Promise(function (resolve) { + setTimeout(resolve.bind(null, value), time); + }); +} +exports.delay = delay; diff --git a/lib/functions/links.d.ts b/lib/functions/links.d.ts new file mode 100644 index 0000000..8782dde --- /dev/null +++ b/lib/functions/links.d.ts @@ -0,0 +1,3 @@ +import { Tasks } from "../types"; +export declare function all(tasks: Tasks): Promise; +export declare function race(tasks: Tasks): Promise; diff --git a/lib/functions/links.js b/lib/functions/links.js new file mode 100644 index 0000000..ca2644d --- /dev/null +++ b/lib/functions/links.js @@ -0,0 +1,9 @@ +"use strict"; +function all(tasks) { + return Promise.all(tasks); +} +exports.all = all; +function race(tasks) { + return Promise.race(tasks); +} +exports.race = race; diff --git a/lib/functions/parallel.d.ts b/lib/functions/parallel.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/lib/functions/parallel.js b/lib/functions/parallel.js new file mode 100644 index 0000000..261097f --- /dev/null +++ b/lib/functions/parallel.js @@ -0,0 +1,13 @@ +/*import { + Tasks +} from "../types"; + +export function parallel(tasks: Tasks, maxThreads: number = tasks.length): Promise { + if (tasks.length === 0) { + return Promise.resolve(); + } + + return new Promise(function (resolve, reject) { + + }); +}*/ diff --git a/lib/functions/retry.d.ts b/lib/functions/retry.d.ts new file mode 100644 index 0000000..ff0bf72 --- /dev/null +++ b/lib/functions/retry.d.ts @@ -0,0 +1,2 @@ +import { ITask } from "../types"; +export declare function retry(task: ITask, times: number): Promise; diff --git a/lib/functions/retry.js b/lib/functions/retry.js new file mode 100644 index 0000000..786383a --- /dev/null +++ b/lib/functions/retry.js @@ -0,0 +1,21 @@ +"use strict"; +function process(task, times, reasons, resolve, reject) { + task().then(function (result) { + resolve(result); + }).catch(function (reason) { + reasons.push(reason); + if (reasons.length >= times) { + reject(reasons); + } + else { + process(task, times, reasons, resolve, reject); + } + }); +} +function retry(task, times) { + return new Promise(function (resolve, reject) { + var reasons = []; + process(task, times, reasons, resolve, reject); + }); +} +exports.retry = retry; diff --git a/lib/functions/timeout.d.ts b/lib/functions/timeout.d.ts new file mode 100644 index 0000000..bb47734 --- /dev/null +++ b/lib/functions/timeout.d.ts @@ -0,0 +1,2 @@ +import { ITask } from "../types"; +export declare function timeout(time: number, task: ITask): Promise; diff --git a/lib/functions/timeout.js b/lib/functions/timeout.js new file mode 100644 index 0000000..e6bf4b0 --- /dev/null +++ b/lib/functions/timeout.js @@ -0,0 +1,15 @@ +"use strict"; +var types_1 = require("../types"); +function timeout(time, task) { + return new Promise(function (resolve, reject) { + var timeout = setTimeout(reject.bind(null, new types_1.TimeoutError()), time); + task().then(function (result) { + resolve(result); + clearTimeout(timeout); + }, function (reason) { + reject(reason); + clearTimeout(timeout); + }); + }); +} +exports.timeout = timeout; diff --git a/lib/functions/times.d.ts b/lib/functions/times.d.ts new file mode 100644 index 0000000..f0f7fed --- /dev/null +++ b/lib/functions/times.d.ts @@ -0,0 +1,2 @@ +import { ITask } from "../types"; +export declare function times(task: ITask, times: number, stopOnFirstReject?: boolean): Promise; diff --git a/lib/functions/times.js b/lib/functions/times.js new file mode 100644 index 0000000..2dc8238 --- /dev/null +++ b/lib/functions/times.js @@ -0,0 +1,33 @@ +"use strict"; +function stopCheck(times, results, resolve) { + if (results.length >= times) { + resolve(results); + return true; + } +} +function process(task, times, results, resolve, reject, stopOnFirstReject) { + task().then(function (result) { + results.push(result); + if (!stopCheck(times, results, resolve)) { + process(task, times, results, resolve, reject, stopOnFirstReject); + } + }).catch(function (reason) { + results.push(reason); + if (stopOnFirstReject) { + reject(results); + } + else if (!stopCheck(times, results, resolve)) { + process(task, times, results, resolve, reject, stopOnFirstReject); + } + }); +} +function times(task, times, stopOnFirstReject) { + if (times <= 0) { + return Promise.resolve([]); + } + return new Promise(function (resolve, reject) { + var results = []; + process(task, times, results, resolve, reject, stopOnFirstReject); + }); +} +exports.times = times; diff --git a/lib/functions/waterfall.d.ts b/lib/functions/waterfall.d.ts new file mode 100644 index 0000000..9c2c13c --- /dev/null +++ b/lib/functions/waterfall.d.ts @@ -0,0 +1,2 @@ +import { Tasks } from "../types"; +export declare function waterfall(tasks: Tasks): Promise; diff --git a/lib/functions/waterfall.js b/lib/functions/waterfall.js new file mode 100644 index 0000000..460e9ab --- /dev/null +++ b/lib/functions/waterfall.js @@ -0,0 +1,15 @@ +"use strict"; +function waterfall(tasks) { + if (tasks.length === 0) { + return Promise.resolve(); + } + var promise = tasks[0](); + if (tasks.length > 1) { + for (var _i = 0, tasks_1 = tasks; _i < tasks_1.length; _i++) { + var task = tasks_1[_i]; + promise = promise.then(task); + } + } + return promise; +} +exports.waterfall = waterfall; diff --git a/lib/prow.d.ts b/lib/prow.d.ts new file mode 100644 index 0000000..d252a29 --- /dev/null +++ b/lib/prow.d.ts @@ -0,0 +1,6 @@ +export { ITask, Tasks, TimeoutError } from "./types"; +export { delay } from "./functions/delay"; +export { timeout } from "./functions/timeout"; +export { waterfall } from "./functions/waterfall"; +export { retry } from "./functions/retry"; +export { times } from "./functions/times"; diff --git a/lib/prow.js b/lib/prow.js new file mode 100644 index 0000000..3b2b393 --- /dev/null +++ b/lib/prow.js @@ -0,0 +1,13 @@ +"use strict"; +var types_1 = require("./types"); +exports.TimeoutError = types_1.TimeoutError; +var delay_1 = require("./functions/delay"); +exports.delay = delay_1.delay; +var timeout_1 = require("./functions/timeout"); +exports.timeout = timeout_1.timeout; +var waterfall_1 = require("./functions/waterfall"); +exports.waterfall = waterfall_1.waterfall; +var retry_1 = require("./functions/retry"); +exports.retry = retry_1.retry; +var times_1 = require("./functions/times"); +exports.times = times_1.times; diff --git a/lib/types.d.ts b/lib/types.d.ts new file mode 100644 index 0000000..d6c7b3d --- /dev/null +++ b/lib/types.d.ts @@ -0,0 +1,6 @@ +export interface ITask { + (...args: any[]): Promise; +} +export declare type Tasks = ITask[]; +export declare class TimeoutError extends Error { +} diff --git a/lib/types.js b/lib/types.js new file mode 100644 index 0000000..3929db9 --- /dev/null +++ b/lib/types.js @@ -0,0 +1,14 @@ +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var TimeoutError = (function (_super) { + __extends(TimeoutError, _super); + function TimeoutError() { + _super.apply(this, arguments); + } + return TimeoutError; +}(Error)); +exports.TimeoutError = TimeoutError; diff --git a/package.json b/package.json index ae2dd24..fa60500 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,10 @@ ], "scripts": { "build": "tsc", + "pretest": "npm run lint", "test": "mocha", - "lint": "tslint --project ./" + "lint": "tslint --project ./", + "prepublish": "npm run lint && npm run build && mocha" }, "devDependencies": { "chai": "^3.5.0", diff --git a/src/functions/parallel.ts b/src/functions/parallel.ts index 3217020..261097f 100644 --- a/src/functions/parallel.ts +++ b/src/functions/parallel.ts @@ -1,15 +1,13 @@ -import { +/*import { Tasks } from "../types"; - -export function parallel(tasks: Tasks, maxThreads?: number): Promise { - if (!maxThreads) { - maxThreads = tasks.length; +export function parallel(tasks: Tasks, maxThreads: number = tasks.length): Promise { + if (tasks.length === 0) { + return Promise.resolve(); } return new Promise(function (resolve, reject) { - }); -} +}*/ diff --git a/src/functions/retry.ts b/src/functions/retry.ts index 746dd57..8425871 100644 --- a/src/functions/retry.ts +++ b/src/functions/retry.ts @@ -1,8 +1,8 @@ import { - Task + ITask } from "../types"; -function process(task: Task, times: number, reasons: any[], resolve, reject) { +function process(task: ITask, times: number, reasons: any[], resolve, reject) { task().then((result) => { resolve(result); }).catch((reason) => { @@ -10,12 +10,12 @@ function process(task: Task, times: number, reasons: any[], resolve, reject) { if (reasons.length >= times) { reject(reasons); } else { - process(task, times, resolve, reject, reasons); + process(task, times, reasons, resolve, reject); } }); } -export function retry(task: Task, times: number): Promise { +export function retry(task: ITask, times: number): Promise { return new Promise(function (resolve, reject) { const reasons = []; process(task, times, reasons, resolve, reject); diff --git a/src/functions/timeout.ts b/src/functions/timeout.ts index 96911ab..e595e12 100644 --- a/src/functions/timeout.ts +++ b/src/functions/timeout.ts @@ -1,8 +1,8 @@ import { - Task, TimeoutError + ITask, TimeoutError } from "../types"; -export function timeout(time: number, task: Task): Promise { +export function timeout(time: number, task: ITask): Promise { return new Promise(function (resolve, reject) { const timeout = setTimeout(reject.bind(null, new TimeoutError()), time); task().then((result) => { diff --git a/src/functions/times.ts b/src/functions/times.ts index 1665594..80cb344 100644 --- a/src/functions/times.ts +++ b/src/functions/times.ts @@ -1,11 +1,37 @@ import { - Task + ITask } from "../types"; +function stopCheck(times: number, results: any[], resolve): boolean { + if (results.length >= times) { + resolve(results); + return true; + } +} + +function process(task: ITask, times: number, results: any[], resolve, reject, stopOnFirstReject: boolean) { + task().then((result) => { + results.push(result); + if (!stopCheck(times, results, resolve)) { + process(task, times, results, resolve, reject, stopOnFirstReject); + } + }).catch((reason) => { + results.push(reason); + if (stopOnFirstReject) { + reject(results); + } else if (!stopCheck(times, results, resolve)) { + process(task, times, results, resolve, reject, stopOnFirstReject); + } + }); +} + +export function times(task: ITask, times: number, stopOnFirstReject?: boolean): Promise { + if (times <= 0) { + return Promise.resolve([]); + } -export function times(task: Task, times: number, stopOnFirstReject: boolean): Promise { return new Promise(function (resolve, reject) { const results = []; - + process(task, times, results, resolve, reject, stopOnFirstReject); }); -} +} \ No newline at end of file diff --git a/src/prow.ts b/src/prow.ts index 6935109..5295d97 100644 --- a/src/prow.ts +++ b/src/prow.ts @@ -1,19 +1,9 @@ export { - Task, Tasks, TimeoutError + ITask, Tasks, TimeoutError } from "./types"; export {delay} from "./functions/delay"; export {timeout} from "./functions/timeout"; export {waterfall} from "./functions/waterfall"; export {retry} from "./functions/retry"; - - - - - - - - - - - +export {times} from "./functions/times"; \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 6c60853..52d8564 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,8 @@ -export interface Task { - (...args: any[]): Promise +export interface ITask { + (...args: any[]): Promise; } -export type Tasks = Task[]; -export class TimeoutError extends Error{ +export type Tasks = ITask[]; + +export class TimeoutError extends Error { } diff --git a/test/delay.js b/test/delay.js new file mode 100644 index 0000000..fea21c2 --- /dev/null +++ b/test/delay.js @@ -0,0 +1,28 @@ +const _ = require("lodash"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +chai.use(chaiAsPromised); +const {assert, expect} = chai; +const prow = require("../lib/prow"); + +describe("Delay", function () { + it("return value", function () { + return assert.becomes(prow.delay(10, 300), 300); + }); + + it("delay 160ms", function () { + const start = process.hrtime(); + return assert.becomes(prow.delay(160, 300), 300).then(() => { + const time = process.hrtime(start); + assert.approximately(time[0] * 100000000 + time[1], 160000000, 5000000) + }); + }); + + it("delay 1050ms", function () { + const start = process.hrtime(); + return assert.becomes(prow.delay(1050, 300), 300).then(() => { + const time = process.hrtime(start); + assert.approximately(time[0] * 1000000000 + time[1], 1050000000, 5000000) + }); + }); +}); diff --git a/test/retry.js b/test/retry.js new file mode 100644 index 0000000..2797e6b --- /dev/null +++ b/test/retry.js @@ -0,0 +1,41 @@ +const _ = require("lodash"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +chai.use(chaiAsPromised); +const {assert, expect} = chai; +const prow = require("../lib/prow"); + +function resolvePromise(value) { + return function (data) { + if (data) { + return Promise.resolve(data + 1); + } + return Promise.resolve(value); + } +} + +function promiseRejected(rejectedTimes, resolveValue) { + let counter = 0; + return function () { + if (counter++ < rejectedTimes) { + return Promise.reject(counter); + } + return Promise.resolve(resolveValue); + } +} + +describe("Retry", function () { + it("resolve", function () { + return assert.becomes(prow.retry(resolvePromise("resolve"), 1), "resolve"); + }); + + it("reject 5 times", function () { + return prow.retry(promiseRejected(5, null), 5).catch((data) => { + assert.deepEqual(data, [1, 2, 3, 4, 5]) + }); + }); + + it("reject 5 times, resolve on 6th", function () { + return assert.becomes(prow.retry(promiseRejected(5, 42), 6), 42); + }); +}); diff --git a/test/timeout.js b/test/timeout.js new file mode 100644 index 0000000..5e74211 --- /dev/null +++ b/test/timeout.js @@ -0,0 +1,34 @@ +const _ = require("lodash"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +chai.use(chaiAsPromised); +const {assert, expect} = chai; +const prow = require("../lib/prow"); + +function resolvePromise(value) { + return function (data) { + if (data) { + return Promise.resolve(data + 1); + } + return Promise.resolve(value); + } +} + +function rejectPromise(value) { + return function () { + return Promise.reject(value); + } +} +describe("Timeout", function () { + it("resolve value", function () { + return assert.becomes(prow.timeout(100, resolvePromise(42)), 42); + }); + + it("reject promise value", function () { + return assert.isRejected(prow.timeout(100, rejectPromise(24)), 24); + }); + + it("reject by timeout", function () { + return assert.isRejected(prow.timeout(10, () => prow.delay(20, 300)), prow.TimeoutError); + }); +}); diff --git a/test/times.js b/test/times.js new file mode 100644 index 0000000..b56222c --- /dev/null +++ b/test/times.js @@ -0,0 +1,65 @@ +const _ = require("lodash"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +chai.use(chaiAsPromised); +const {assert, expect} = chai; +const prow = require("../lib/prow"); + + +function resolvePromise(value) { + return function (data) { + if (data) { + return Promise.resolve(data + 1); + } + return Promise.resolve(value); + } +} + +function rejectPromise(value) { + return function () { + return Promise.reject(value); + } +} + +function promise3time(resolveValue, rejectValue) { + let counter = 0; + return function () { + if (counter++ >= 3) { + return Promise.reject(rejectValue); + } + return Promise.resolve(resolveValue); + } +} + +function promiseReturnCounter() { + let counter = 0; + return function () { + return Promise.resolve(counter++); + } +} + +describe("Times", function () { + it("0 times", function () { + return assert.becomes(prow.times(resolvePromise(42), 0), []); + }); + + it("1 time", function () { + return assert.becomes(prow.times(resolvePromise(42), 1), [42]); + }); + + it("10 times", function () { + return assert.becomes(prow.times(promiseReturnCounter(), 10), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + it("5 times, rejected", function () { + return assert.becomes(prow.times(rejectPromise(24), 5), [24, 24, 24, 24, 24]); + }); + + it("5 times, rejected on 4th and next, stopOnFirstReject off", function () { + return assert.becomes(prow.times(promise3time(true, false), 5), [true, true, true, false, false]); + }); + + it("5 times, rejected on 4th and next, stopOnFirstReject on", function () { + return assert.isRejected(prow.times(promise3time(true, false), 5, true), [true, true, true, false]); + }); +}); From dbd1efe551578dcaf630ac36c1409bdc643a5b28 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Tue, 1 Nov 2016 23:46:22 +0500 Subject: [PATCH 05/14] travis bump --- test/retry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/retry.js b/test/retry.js index 2797e6b..4116b4d 100644 --- a/test/retry.js +++ b/test/retry.js @@ -38,4 +38,4 @@ describe("Retry", function () { it("reject 5 times, resolve on 6th", function () { return assert.becomes(prow.retry(promiseRejected(5, 42), 6), 42); }); -}); +}); \ No newline at end of file From f15d4fed4404b791ea6bff822b3e6dc10e704a31 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Tue, 1 Nov 2016 23:52:33 +0500 Subject: [PATCH 06/14] travis bump --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dbdaa9a..b00ab1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - '4.0' + - '5.0' - 'stable' sudo: false From b75de9e7b2fc1174e75a8191599920e90e1ac231 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Wed, 2 Nov 2016 00:07:06 +0500 Subject: [PATCH 07/14] travis bump --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b00ab1d..33e183d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - '5.0' + - '6.0' - 'stable' sudo: false From 995e18ad002d6fc1ba1e4a33b44476e4b42db03b Mon Sep 17 00:00:00 2001 From: rzcoder Date: Thu, 3 Nov 2016 01:08:30 +0500 Subject: [PATCH 08/14] parallel --- lib/functions/parallel.d.ts | 3 ++ lib/functions/parallel.js | 49 +++++++++++++++---- lib/prow.d.ts | 1 + lib/prow.js | 3 ++ src/functions/parallel.ts | 53 +++++++++++++++++++-- src/prow.ts | 3 +- test/parallel.js | 93 +++++++++++++++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 test/parallel.js diff --git a/lib/functions/parallel.d.ts b/lib/functions/parallel.d.ts index e69de29..1a841b0 100644 --- a/lib/functions/parallel.d.ts +++ b/lib/functions/parallel.d.ts @@ -0,0 +1,3 @@ +import { Tasks } from "../types"; +export declare function parallel(tasks: Tasks, maxThreads?: number): Promise; +export declare function queue(tasks: Tasks): Promise; diff --git a/lib/functions/parallel.js b/lib/functions/parallel.js index 261097f..e3ee839 100644 --- a/lib/functions/parallel.js +++ b/lib/functions/parallel.js @@ -1,13 +1,46 @@ -/*import { - Tasks -} from "../types"; - -export function parallel(tasks: Tasks, maxThreads: number = tasks.length): Promise { +"use strict"; +function promiseHandler(index, data) { + this.results[index] = data; + this.processes--; + if (this.processes === 0 && index === this.tasks.length - 1) { + this.resolve(this.results); + } + else { + execute(this); + } +} +function execute(scope) { + if (scope.processes < scope.maxThreads && scope.pointer < scope.tasks.length) { + var handler = promiseHandler.bind(scope, scope.pointer); + scope.tasks[scope.pointer]().then(handler, handler); + scope.pointer++; + scope.processes++; + } +} +function parallel(tasks, maxThreads) { + if (maxThreads === void 0) { maxThreads = tasks.length; } if (tasks.length === 0) { return Promise.resolve(); } - + var scope = { + processes: 0, + pointer: 0, + tasks: tasks, + maxThreads: maxThreads, + results: [], + resolve: null, + reject: null + }; return new Promise(function (resolve, reject) { - + for (var i = 0; i < maxThreads && i < tasks.length; i++) { + scope.resolve = resolve; + scope.reject = reject; + execute(scope); + } }); -}*/ +} +exports.parallel = parallel; +function queue(tasks) { + return parallel(tasks, 1); +} +exports.queue = queue; diff --git a/lib/prow.d.ts b/lib/prow.d.ts index d252a29..196febf 100644 --- a/lib/prow.d.ts +++ b/lib/prow.d.ts @@ -4,3 +4,4 @@ export { timeout } from "./functions/timeout"; export { waterfall } from "./functions/waterfall"; export { retry } from "./functions/retry"; export { times } from "./functions/times"; +export { parallel, queue } from "./functions/parallel"; diff --git a/lib/prow.js b/lib/prow.js index 3b2b393..9416a66 100644 --- a/lib/prow.js +++ b/lib/prow.js @@ -11,3 +11,6 @@ var retry_1 = require("./functions/retry"); exports.retry = retry_1.retry; var times_1 = require("./functions/times"); exports.times = times_1.times; +var parallel_1 = require("./functions/parallel"); +exports.parallel = parallel_1.parallel; +exports.queue = parallel_1.queue; diff --git a/src/functions/parallel.ts b/src/functions/parallel.ts index 261097f..df93a11 100644 --- a/src/functions/parallel.ts +++ b/src/functions/parallel.ts @@ -1,13 +1,60 @@ -/*import { +import { Tasks } from "../types"; +interface IScope { + processes: number; + pointer: number; + tasks: Tasks; + maxThreads: number; + results: any[]; + resolve: any; + reject: any; +} + +function promiseHandler (index: number, data: any) { + this.results[index] = data; + this.processes--; + if (this.processes === 0 && index === this.tasks.length - 1) { + this.resolve(this.results); + } else { + execute(this); + } +} + +function execute(scope: IScope) { + if (scope.processes < scope.maxThreads && scope.pointer < scope.tasks.length) { + const handler = promiseHandler.bind(scope, scope.pointer); + scope.tasks[scope.pointer]().then(handler, handler); + scope.pointer++; + scope.processes++; + } +} + export function parallel(tasks: Tasks, maxThreads: number = tasks.length): Promise { if (tasks.length === 0) { return Promise.resolve(); } - return new Promise(function (resolve, reject) { + const scope: IScope = { + processes: 0, + pointer: 0, + tasks: tasks, + maxThreads: maxThreads, + results: [], + resolve: null, + reject: null + }; + return new Promise(function (resolve, reject) { + for (let i = 0; i < maxThreads && i < tasks.length; i++) { + scope.resolve = resolve; + scope.reject = reject; + execute(scope); + } }); -}*/ +} + +export function queue(tasks: Tasks): Promise { + return parallel(tasks, 1); +} \ No newline at end of file diff --git a/src/prow.ts b/src/prow.ts index 5295d97..a8b5c71 100644 --- a/src/prow.ts +++ b/src/prow.ts @@ -6,4 +6,5 @@ export {delay} from "./functions/delay"; export {timeout} from "./functions/timeout"; export {waterfall} from "./functions/waterfall"; export {retry} from "./functions/retry"; -export {times} from "./functions/times"; \ No newline at end of file +export {times} from "./functions/times"; +export {parallel, queue} from "./functions/parallel"; \ No newline at end of file diff --git a/test/parallel.js b/test/parallel.js new file mode 100644 index 0000000..1e0c895 --- /dev/null +++ b/test/parallel.js @@ -0,0 +1,93 @@ +const _ = require("lodash"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +chai.use(chaiAsPromised); +const {assert, expect} = chai; +const prow = require("../lib/prow"); + +describe("Parallel", function () { + it("queue", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => prow.delay(1).then(() => counter++)); + } + + return assert.becomes(prow.queue(tasks), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + it("parallel 1 thread", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => prow.delay(1).then(() => counter++)); + } + + return assert.becomes(prow.parallel(tasks, 1), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + it("parallel 2 threads", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => { + counter++; + return Promise.resolve().then(() => counter--) + }); + } + + return assert.becomes(prow.parallel(tasks, 2), [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]); + }); + + it("parallel 5 threads", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => { + counter++; + return Promise.resolve().then(() => counter--) + }); + } + + return assert.becomes(prow.parallel(tasks, 5), [5, 4, 3, 2, 1, 5, 4, 3, 2, 1]); + }); + + it("parallel 7 threads", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => { + counter++; + return Promise.resolve().then(() => counter--) + }); + } + + return assert.becomes(prow.parallel(tasks, 7), [7, 6, 5, 4, 3, 2, 1, 3, 2, 1]); + }); + + it("parallel 10 threads", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => { + counter++; + return Promise.resolve().then(() => counter--) + }); + } + + return assert.becomes(prow.parallel(tasks), [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]); + }); + + it("parallel 2 threads wit rejects", function () { + const tasks = []; + let counter = 0; + for (let i = 0; i < 10; i++) { + tasks.push(() => { + counter++; + return Promise.reject().catch(() => counter--) + }); + } + + return assert.becomes(prow.parallel(tasks, 2), [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]); + }); +}); From 4a54e8c4d282ad894d98435480ec28c6418c8955 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Thu, 3 Nov 2016 01:10:10 +0500 Subject: [PATCH 09/14] travis update --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 33e183d..5243d41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,5 @@ node_js: sudo: false -before_install: - - npm install -g npm@latest - - npm install -g grunt-cli - install: - npm install \ No newline at end of file From 65a083bb619d58ba9500841c038127092e7abb1e Mon Sep 17 00:00:00 2001 From: rzcoder Date: Thu, 3 Nov 2016 01:10:34 +0500 Subject: [PATCH 10/14] typo --- test/parallel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel.js b/test/parallel.js index 1e0c895..469de35 100644 --- a/test/parallel.js +++ b/test/parallel.js @@ -78,7 +78,7 @@ describe("Parallel", function () { return assert.becomes(prow.parallel(tasks), [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]); }); - it("parallel 2 threads wit rejects", function () { + it("parallel 2 threads with rejects", function () { const tasks = []; let counter = 0; for (let i = 0; i < 10; i++) { From 2f75385fddd961b97206d04752800159130d889c Mon Sep 17 00:00:00 2001 From: rzcoder Date: Thu, 3 Nov 2016 01:13:47 +0500 Subject: [PATCH 11/14] scripts update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa60500..b049481 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "pretest": "npm run lint", "test": "mocha", "lint": "tslint --project ./", - "prepublish": "npm run lint && npm run build && mocha" + "prepublish": "npm run build" }, "devDependencies": { "chai": "^3.5.0", From aa2d7483576c92ebab7bf67bdafa01f70f255b36 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Mon, 7 Nov 2016 18:30:23 +0500 Subject: [PATCH 12/14] await --- lib/functions/delay.d.ts | 6 ++++++ lib/functions/delay.js | 6 ++++++ lib/functions/retry.d.ts | 2 +- lib/functions/retry.js | 20 ++++++++++++++------ lib/functions/timeout.d.ts | 2 +- lib/functions/timeout.js | 7 +++++-- lib/prow.d.ts | 1 + lib/prow.js | 2 ++ lib/types.d.ts | 1 + src/functions/await.ts | 25 +++++++++++++++++++++++++ src/functions/delay.ts | 6 ++++++ src/functions/retry.ts | 18 ++++++++++++------ src/functions/timeout.ts | 8 ++++++-- src/prow.ts | 3 ++- test/delay.js | 6 +++--- test/timeout.js | 8 ++++---- 16 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 src/functions/await.ts diff --git a/lib/functions/delay.d.ts b/lib/functions/delay.d.ts index 23b81bd..2a473f5 100644 --- a/lib/functions/delay.d.ts +++ b/lib/functions/delay.d.ts @@ -1 +1,7 @@ +/** + * Delayed resolving promise + * @param time Time in ms before promise will resolved + * @param value Value to be returned in Promise.resolve + * @returns Promise + */ export declare function delay(time: number, value?: any): Promise; diff --git a/lib/functions/delay.js b/lib/functions/delay.js index 57e1fa5..81930b8 100644 --- a/lib/functions/delay.js +++ b/lib/functions/delay.js @@ -1,4 +1,10 @@ "use strict"; +/** + * Delayed resolving promise + * @param time Time in ms before promise will resolved + * @param value Value to be returned in Promise.resolve + * @returns Promise + */ function delay(time, value) { return new Promise(function (resolve) { setTimeout(resolve.bind(null, value), time); diff --git a/lib/functions/retry.d.ts b/lib/functions/retry.d.ts index ff0bf72..d99b71f 100644 --- a/lib/functions/retry.d.ts +++ b/lib/functions/retry.d.ts @@ -1,2 +1,2 @@ import { ITask } from "../types"; -export declare function retry(task: ITask, times: number): Promise; +export declare function retry(task: ITask, times?: number, delay?: number): Promise; diff --git a/lib/functions/retry.js b/lib/functions/retry.js index 786383a..03a1b32 100644 --- a/lib/functions/retry.js +++ b/lib/functions/retry.js @@ -1,21 +1,29 @@ "use strict"; -function process(task, times, reasons, resolve, reject) { - task().then(function (result) { +var prow = require("../prow"); +function process(task, times, reasons, delay, resolve, reject) { + return task().then(function (result) { resolve(result); }).catch(function (reason) { reasons.push(reason); - if (reasons.length >= times) { + if (reasons.length >= times && times >= 0) { reject(reasons); } else { - process(task, times, reasons, resolve, reject); + if (delay > 0) { + prow.delay(delay).then(function () { return process(task, times, reasons, delay, resolve, reject); }); + } + else { + process(task, times, reasons, delay, resolve, reject); + } } }); } -function retry(task, times) { +function retry(task, times, delay) { + if (times === void 0) { times = -1; } + if (delay === void 0) { delay = 0; } return new Promise(function (resolve, reject) { var reasons = []; - process(task, times, reasons, resolve, reject); + process(task, times, reasons, delay, resolve, reject); }); } exports.retry = retry; diff --git a/lib/functions/timeout.d.ts b/lib/functions/timeout.d.ts index bb47734..fc4a318 100644 --- a/lib/functions/timeout.d.ts +++ b/lib/functions/timeout.d.ts @@ -1,2 +1,2 @@ import { ITask } from "../types"; -export declare function timeout(time: number, task: ITask): Promise; +export declare function timeout(task: ITask, time: number): Promise; diff --git a/lib/functions/timeout.js b/lib/functions/timeout.js index e6bf4b0..f16c777 100644 --- a/lib/functions/timeout.js +++ b/lib/functions/timeout.js @@ -1,8 +1,11 @@ "use strict"; var types_1 = require("../types"); -function timeout(time, task) { +function timeout(task, time) { return new Promise(function (resolve, reject) { - var timeout = setTimeout(reject.bind(null, new types_1.TimeoutError()), time); + var timeout = -1; + if (time >= 0) { + timeout = setTimeout(reject.bind(null, new types_1.TimeoutError()), time); + } task().then(function (result) { resolve(result); clearTimeout(timeout); diff --git a/lib/prow.d.ts b/lib/prow.d.ts index 196febf..49430c2 100644 --- a/lib/prow.d.ts +++ b/lib/prow.d.ts @@ -5,3 +5,4 @@ export { waterfall } from "./functions/waterfall"; export { retry } from "./functions/retry"; export { times } from "./functions/times"; export { parallel, queue } from "./functions/parallel"; +export { await } from "./functions/await"; diff --git a/lib/prow.js b/lib/prow.js index 9416a66..0ca0ade 100644 --- a/lib/prow.js +++ b/lib/prow.js @@ -14,3 +14,5 @@ exports.times = times_1.times; var parallel_1 = require("./functions/parallel"); exports.parallel = parallel_1.parallel; exports.queue = parallel_1.queue; +var await_1 = require("./functions/await"); +exports.await = await_1.await; diff --git a/lib/types.d.ts b/lib/types.d.ts index d6c7b3d..085a59d 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -1,3 +1,4 @@ +/// export interface ITask { (...args: any[]): Promise; } diff --git a/src/functions/await.ts b/src/functions/await.ts new file mode 100644 index 0000000..d052b70 --- /dev/null +++ b/src/functions/await.ts @@ -0,0 +1,25 @@ +import { + ITask +} from "../types"; + +import * as prow from "../prow"; + +/** + * Returns an Promise which will resolve when the condition is satisfied, or rejected if timeout expired + * @param condition Task which should resolve with check result + * @param delay Delay between when condition task return value and run new one + * @param timeout Timeout before promise will rejected. `-1` for endless waiting. + * @returns Promise + */ +export function await(condition: ITask, delay: number, timeout: number = -1): Promise { + const promise = new Promise(function (resolve) { + const conditionHandler = (result) => { + if (result) { + resolve(); + } + }; + prow.retry(condition, -1, delay).then(conditionHandler); + }); + + return prow.timeout(() => promise, timeout); +} \ No newline at end of file diff --git a/src/functions/delay.ts b/src/functions/delay.ts index 3c68217..c56f2fe 100644 --- a/src/functions/delay.ts +++ b/src/functions/delay.ts @@ -1,3 +1,9 @@ +/** + * Delayed resolving promise + * @param time Time in ms before promise will resolved + * @param value Value to be returned in Promise.resolve + * @returns Promise + */ export function delay(time: number, value?: any): Promise { return new Promise(function (resolve) { setTimeout(resolve.bind(null, value), time); diff --git a/src/functions/retry.ts b/src/functions/retry.ts index 8425871..2df3785 100644 --- a/src/functions/retry.ts +++ b/src/functions/retry.ts @@ -2,22 +2,28 @@ import { ITask } from "../types"; -function process(task: ITask, times: number, reasons: any[], resolve, reject) { - task().then((result) => { +import * as prow from "../prow"; + +function process(task: ITask, times: number, reasons: any[], delay, resolve, reject): Promise { + return task().then((result) => { resolve(result); }).catch((reason) => { reasons.push(reason); - if (reasons.length >= times) { + if (reasons.length >= times && times >= 0) { reject(reasons); } else { - process(task, times, reasons, resolve, reject); + if (delay > 0) { + prow.delay(delay).then(() => process(task, times, reasons, delay, resolve, reject)); + } else { + process(task, times, reasons, delay, resolve, reject); + } } }); } -export function retry(task: ITask, times: number): Promise { +export function retry(task: ITask, times: number = -1, delay: number = 0): Promise { return new Promise(function (resolve, reject) { const reasons = []; - process(task, times, reasons, resolve, reject); + process(task, times, reasons, delay, resolve, reject); }); } diff --git a/src/functions/timeout.ts b/src/functions/timeout.ts index e595e12..628ce3f 100644 --- a/src/functions/timeout.ts +++ b/src/functions/timeout.ts @@ -2,9 +2,13 @@ import { ITask, TimeoutError } from "../types"; -export function timeout(time: number, task: ITask): Promise { +export function timeout(task: ITask, time: number): Promise { return new Promise(function (resolve, reject) { - const timeout = setTimeout(reject.bind(null, new TimeoutError()), time); + let timeout = -1; + if (time >= 0) { + timeout = setTimeout(reject.bind(null, new TimeoutError()), time); + } + task().then((result) => { resolve(result); clearTimeout(timeout); diff --git a/src/prow.ts b/src/prow.ts index a8b5c71..1e577b2 100644 --- a/src/prow.ts +++ b/src/prow.ts @@ -7,4 +7,5 @@ export {timeout} from "./functions/timeout"; export {waterfall} from "./functions/waterfall"; export {retry} from "./functions/retry"; export {times} from "./functions/times"; -export {parallel, queue} from "./functions/parallel"; \ No newline at end of file +export {parallel, queue} from "./functions/parallel"; +export {await} from "./functions/await"; \ No newline at end of file diff --git a/test/delay.js b/test/delay.js index fea21c2..45d6b19 100644 --- a/test/delay.js +++ b/test/delay.js @@ -2,7 +2,7 @@ const _ = require("lodash"); const chai = require("chai"); const chaiAsPromised = require("chai-as-promised"); chai.use(chaiAsPromised); -const {assert, expect} = chai; +const {assert} = chai; const prow = require("../lib/prow"); describe("Delay", function () { @@ -14,7 +14,7 @@ describe("Delay", function () { const start = process.hrtime(); return assert.becomes(prow.delay(160, 300), 300).then(() => { const time = process.hrtime(start); - assert.approximately(time[0] * 100000000 + time[1], 160000000, 5000000) + assert.approximately(time[0] * 100000000 + time[1], 160000000, 6000000) }); }); @@ -22,7 +22,7 @@ describe("Delay", function () { const start = process.hrtime(); return assert.becomes(prow.delay(1050, 300), 300).then(() => { const time = process.hrtime(start); - assert.approximately(time[0] * 1000000000 + time[1], 1050000000, 5000000) + assert.approximately(time[0] * 1000000000 + time[1], 1050000000, 6000000) }); }); }); diff --git a/test/timeout.js b/test/timeout.js index 5e74211..b50a1cb 100644 --- a/test/timeout.js +++ b/test/timeout.js @@ -2,7 +2,7 @@ const _ = require("lodash"); const chai = require("chai"); const chaiAsPromised = require("chai-as-promised"); chai.use(chaiAsPromised); -const {assert, expect} = chai; +const {assert} = chai; const prow = require("../lib/prow"); function resolvePromise(value) { @@ -21,14 +21,14 @@ function rejectPromise(value) { } describe("Timeout", function () { it("resolve value", function () { - return assert.becomes(prow.timeout(100, resolvePromise(42)), 42); + return assert.becomes(prow.timeout(resolvePromise(42), 100), 42); }); it("reject promise value", function () { - return assert.isRejected(prow.timeout(100, rejectPromise(24)), 24); + return assert.isRejected(prow.timeout(rejectPromise(24), 100), 24); }); it("reject by timeout", function () { - return assert.isRejected(prow.timeout(10, () => prow.delay(20, 300)), prow.TimeoutError); + return assert.isRejected(prow.timeout(() => prow.delay(20, 300), 10), prow.TimeoutError); }); }); From 794cc8b8603e327b6fc16f2aaf2cb04b5fda33fc Mon Sep 17 00:00:00 2001 From: rzcoder Date: Wed, 9 Nov 2016 01:17:09 +0500 Subject: [PATCH 13/14] await --- lib/functions/parallel.js | 6 ++-- lib/functions/retry.d.ts | 2 +- lib/functions/retry.js | 45 +++++++++++++++++++++-------- lib/functions/timeout.d.ts | 2 +- lib/functions/timeout.js | 12 ++++---- lib/types.d.ts | 1 - src/functions/await.ts | 6 ++-- src/functions/parallel.ts | 6 ++-- src/functions/retry.ts | 59 +++++++++++++++++++++++++++++--------- src/functions/timeout.ts | 12 ++++---- test/retry.js | 26 +++++++++++++++-- 11 files changed, 124 insertions(+), 53 deletions(-) diff --git a/lib/functions/parallel.js b/lib/functions/parallel.js index e3ee839..5bfec76 100644 --- a/lib/functions/parallel.js +++ b/lib/functions/parallel.js @@ -6,10 +6,10 @@ function promiseHandler(index, data) { this.resolve(this.results); } else { - execute(this); + process(this); } } -function execute(scope) { +function process(scope) { if (scope.processes < scope.maxThreads && scope.pointer < scope.tasks.length) { var handler = promiseHandler.bind(scope, scope.pointer); scope.tasks[scope.pointer]().then(handler, handler); @@ -35,7 +35,7 @@ function parallel(tasks, maxThreads) { for (var i = 0; i < maxThreads && i < tasks.length; i++) { scope.resolve = resolve; scope.reject = reject; - execute(scope); + process(scope); } }); } diff --git a/lib/functions/retry.d.ts b/lib/functions/retry.d.ts index d99b71f..3b0bcaf 100644 --- a/lib/functions/retry.d.ts +++ b/lib/functions/retry.d.ts @@ -1,2 +1,2 @@ import { ITask } from "../types"; -export declare function retry(task: ITask, times?: number, delay?: number): Promise; +export declare function retry(task: ITask, times?: number, delay?: number, timeout?: number): Promise; diff --git a/lib/functions/retry.js b/lib/functions/retry.js index 03a1b32..f1176f9 100644 --- a/lib/functions/retry.js +++ b/lib/functions/retry.js @@ -1,29 +1,50 @@ "use strict"; +var types_1 = require("../types"); var prow = require("../prow"); -function process(task, times, reasons, delay, resolve, reject) { - return task().then(function (result) { - resolve(result); +function process(scope) { + return scope.task().then(function (result) { + clearTimeout(scope.timeoutId); + scope.resolve(result); }).catch(function (reason) { - reasons.push(reason); - if (reasons.length >= times && times >= 0) { - reject(reasons); + scope.reasons.push(reason); + if (scope.reasons.length >= scope.times && scope.times >= 0) { + clearTimeout(scope.timeoutId); + scope.reject(scope.reasons); } - else { - if (delay > 0) { - prow.delay(delay).then(function () { return process(task, times, reasons, delay, resolve, reject); }); + else if (!scope.cancelled) { + if (scope.delay > 0) { + prow.delay(scope.delay).then(function () { return process(scope); }); } else { - process(task, times, reasons, delay, resolve, reject); + process(scope); } } }); } -function retry(task, times, delay) { +function retry(task, times, delay, timeout) { if (times === void 0) { times = -1; } if (delay === void 0) { delay = 0; } + if (timeout === void 0) { timeout = -1; } return new Promise(function (resolve, reject) { var reasons = []; - process(task, times, reasons, delay, resolve, reject); + var scope = { + task: task, + times: times, + reasons: reasons, + delay: delay, + timeout: timeout, + cancelled: false, + timeoutId: -1, + resolve: resolve, + reject: reject + }; + if (timeout >= 0) { + scope.timeoutId = setTimeout(function () { + scope.cancelled = true; + reject(new types_1.TimeoutError()); + }, timeout); + } + process(scope); }); } exports.retry = retry; diff --git a/lib/functions/timeout.d.ts b/lib/functions/timeout.d.ts index fc4a318..27b5a44 100644 --- a/lib/functions/timeout.d.ts +++ b/lib/functions/timeout.d.ts @@ -1,2 +1,2 @@ import { ITask } from "../types"; -export declare function timeout(task: ITask, time: number): Promise; +export declare function timeout(task: ITask, timeout: number): Promise; diff --git a/lib/functions/timeout.js b/lib/functions/timeout.js index f16c777..bdc1848 100644 --- a/lib/functions/timeout.js +++ b/lib/functions/timeout.js @@ -1,17 +1,17 @@ "use strict"; var types_1 = require("../types"); -function timeout(task, time) { +function timeout(task, timeout) { return new Promise(function (resolve, reject) { - var timeout = -1; - if (time >= 0) { - timeout = setTimeout(reject.bind(null, new types_1.TimeoutError()), time); + var timeoutId = -1; + if (timeout >= 0) { + timeoutId = setTimeout(reject.bind(null, new types_1.TimeoutError()), timeout); } task().then(function (result) { resolve(result); - clearTimeout(timeout); + clearTimeout(timeoutId); }, function (reason) { reject(reason); - clearTimeout(timeout); + clearTimeout(timeoutId); }); }); } diff --git a/lib/types.d.ts b/lib/types.d.ts index 085a59d..d6c7b3d 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -1,4 +1,3 @@ -/// export interface ITask { (...args: any[]): Promise; } diff --git a/src/functions/await.ts b/src/functions/await.ts index d052b70..e843d74 100644 --- a/src/functions/await.ts +++ b/src/functions/await.ts @@ -12,14 +12,12 @@ import * as prow from "../prow"; * @returns Promise */ export function await(condition: ITask, delay: number, timeout: number = -1): Promise { - const promise = new Promise(function (resolve) { + return new Promise(function (resolve, reject) { const conditionHandler = (result) => { if (result) { resolve(); } }; - prow.retry(condition, -1, delay).then(conditionHandler); + prow.retry(condition, -1, delay, timeout).then(conditionHandler).catch(reject); }); - - return prow.timeout(() => promise, timeout); } \ No newline at end of file diff --git a/src/functions/parallel.ts b/src/functions/parallel.ts index df93a11..584d0fc 100644 --- a/src/functions/parallel.ts +++ b/src/functions/parallel.ts @@ -18,11 +18,11 @@ function promiseHandler (index: number, data: any) { if (this.processes === 0 && index === this.tasks.length - 1) { this.resolve(this.results); } else { - execute(this); + process(this); } } -function execute(scope: IScope) { +function process(scope: IScope) { if (scope.processes < scope.maxThreads && scope.pointer < scope.tasks.length) { const handler = promiseHandler.bind(scope, scope.pointer); scope.tasks[scope.pointer]().then(handler, handler); @@ -50,7 +50,7 @@ export function parallel(tasks: Tasks, maxThreads: number = tasks.length): Promi for (let i = 0; i < maxThreads && i < tasks.length; i++) { scope.resolve = resolve; scope.reject = reject; - execute(scope); + process(scope); } }); } diff --git a/src/functions/retry.ts b/src/functions/retry.ts index 2df3785..fd01793 100644 --- a/src/functions/retry.ts +++ b/src/functions/retry.ts @@ -1,29 +1,62 @@ import { - ITask + ITask, TimeoutError } from "../types"; import * as prow from "../prow"; -function process(task: ITask, times: number, reasons: any[], delay, resolve, reject): Promise { - return task().then((result) => { - resolve(result); +interface IScope { + task: ITask; + times: number; + reasons: any[]; + delay: number; + timeout: number; + cancelled: boolean; + timeoutId: number; + resolve: any; + reject: any; +} + +function process(scope: IScope): Promise { + return scope.task().then((result) => { + clearTimeout(scope.timeoutId); + scope.resolve(result); }).catch((reason) => { - reasons.push(reason); - if (reasons.length >= times && times >= 0) { - reject(reasons); - } else { - if (delay > 0) { - prow.delay(delay).then(() => process(task, times, reasons, delay, resolve, reject)); + scope.reasons.push(reason); + if (scope.reasons.length >= scope.times && scope.times >= 0) { + clearTimeout(scope.timeoutId); + scope.reject(scope.reasons); + } else if (!scope.cancelled) { + if (scope.delay > 0) { + prow.delay(scope.delay).then(() => process(scope)); } else { - process(task, times, reasons, delay, resolve, reject); + process(scope); } } }); } -export function retry(task: ITask, times: number = -1, delay: number = 0): Promise { +export function retry(task: ITask, times: number = -1, delay: number = 0, timeout: number = -1): Promise { return new Promise(function (resolve, reject) { const reasons = []; - process(task, times, reasons, delay, resolve, reject); + const scope = { + task, + times, + reasons, + delay, + timeout, + cancelled: false, + timeoutId: -1, + resolve, + reject + }; + + if (timeout >= 0) { + scope.timeoutId = setTimeout(() => { + scope.cancelled = true; + reject(new TimeoutError()); + }, timeout); + } + + process(scope); }); } diff --git a/src/functions/timeout.ts b/src/functions/timeout.ts index 628ce3f..e1d73d7 100644 --- a/src/functions/timeout.ts +++ b/src/functions/timeout.ts @@ -2,19 +2,19 @@ import { ITask, TimeoutError } from "../types"; -export function timeout(task: ITask, time: number): Promise { +export function timeout(task: ITask, timeout: number): Promise { return new Promise(function (resolve, reject) { - let timeout = -1; - if (time >= 0) { - timeout = setTimeout(reject.bind(null, new TimeoutError()), time); + let timeoutId = -1; + if (timeout >= 0) { + timeoutId = setTimeout(reject.bind(null, new TimeoutError()), timeout); } task().then((result) => { resolve(result); - clearTimeout(timeout); + clearTimeout(timeoutId); }, (reason) => { reject(reason); - clearTimeout(timeout); + clearTimeout(timeoutId); }); }); } \ No newline at end of file diff --git a/test/retry.js b/test/retry.js index 4116b4d..e966a68 100644 --- a/test/retry.js +++ b/test/retry.js @@ -14,7 +14,7 @@ function resolvePromise(value) { } } -function promiseRejected(rejectedTimes, resolveValue) { +function promiseReject(rejectedTimes, resolveValue) { let counter = 0; return function () { if (counter++ < rejectedTimes) { @@ -24,18 +24,38 @@ function promiseRejected(rejectedTimes, resolveValue) { } } +function delayedReject(delayTime, rejectValue) { + return function() { + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(rejectValue); + }, delayTime); + }); + } +} + describe("Retry", function () { it("resolve", function () { return assert.becomes(prow.retry(resolvePromise("resolve"), 1), "resolve"); }); it("reject 5 times", function () { - return prow.retry(promiseRejected(5, null), 5).catch((data) => { + return prow.retry(promiseReject(5, null), 5).catch((data) => { assert.deepEqual(data, [1, 2, 3, 4, 5]) }); }); it("reject 5 times, resolve on 6th", function () { - return assert.becomes(prow.retry(promiseRejected(5, 42), 6), 42); + return assert.becomes(prow.retry(promiseReject(5, 42), 6), 42); + }); + + it("reject by timeout", function () { + return assert.isRejected(prow.retry(delayedReject(50, null), 5, 0, 200), prow.TimeoutError); + }); + + it("timeout, not rejected", function () { + return prow.retry(delayedReject(50, null), 5, 0, 300).catch((data) => { + assert.deepEqual(data, [null, null, null, null, null]) + }); }); }); \ No newline at end of file From 2610148e1725a8a4b5c76c33abe4088c2abb1a5b Mon Sep 17 00:00:00 2001 From: rzcoder Date: Wed, 9 Nov 2016 01:23:40 +0500 Subject: [PATCH 14/14] await --- lib/functions/await.d.ts | 9 +++++++++ lib/functions/await.js | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 lib/functions/await.d.ts create mode 100644 lib/functions/await.js diff --git a/lib/functions/await.d.ts b/lib/functions/await.d.ts new file mode 100644 index 0000000..f8bd332 --- /dev/null +++ b/lib/functions/await.d.ts @@ -0,0 +1,9 @@ +import { ITask } from "../types"; +/** + * Returns an Promise which will resolve when the condition is satisfied, or rejected if timeout expired + * @param condition Task which should resolve with check result + * @param delay Delay between when condition task return value and run new one + * @param timeout Timeout before promise will rejected. `-1` for endless waiting. + * @returns Promise + */ +export declare function await(condition: ITask, delay: number, timeout?: number): Promise; diff --git a/lib/functions/await.js b/lib/functions/await.js new file mode 100644 index 0000000..fe349d9 --- /dev/null +++ b/lib/functions/await.js @@ -0,0 +1,21 @@ +"use strict"; +var prow = require("../prow"); +/** + * Returns an Promise which will resolve when the condition is satisfied, or rejected if timeout expired + * @param condition Task which should resolve with check result + * @param delay Delay between when condition task return value and run new one + * @param timeout Timeout before promise will rejected. `-1` for endless waiting. + * @returns Promise + */ +function await(condition, delay, timeout) { + if (timeout === void 0) { timeout = -1; } + return new Promise(function (resolve, reject) { + var conditionHandler = function (result) { + if (result) { + resolve(); + } + }; + prow.retry(condition, -1, delay, timeout).then(conditionHandler).catch(reject); + }); +} +exports.await = await;