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/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5243d41 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - '6.0' + - 'stable' + +sudo: false + +install: + - npm install \ 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/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; diff --git a/lib/functions/delay.d.ts b/lib/functions/delay.d.ts new file mode 100644 index 0000000..2a473f5 --- /dev/null +++ b/lib/functions/delay.d.ts @@ -0,0 +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 new file mode 100644 index 0000000..81930b8 --- /dev/null +++ b/lib/functions/delay.js @@ -0,0 +1,13 @@ +"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); + }); +} +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..1a841b0 --- /dev/null +++ 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 new file mode 100644 index 0000000..5bfec76 --- /dev/null +++ b/lib/functions/parallel.js @@ -0,0 +1,46 @@ +"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 { + process(this); + } +} +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); + 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; + process(scope); + } + }); +} +exports.parallel = parallel; +function queue(tasks) { + return parallel(tasks, 1); +} +exports.queue = queue; diff --git a/lib/functions/retry.d.ts b/lib/functions/retry.d.ts new file mode 100644 index 0000000..3b0bcaf --- /dev/null +++ b/lib/functions/retry.d.ts @@ -0,0 +1,2 @@ +import { ITask } from "../types"; +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 new file mode 100644 index 0000000..f1176f9 --- /dev/null +++ b/lib/functions/retry.js @@ -0,0 +1,50 @@ +"use strict"; +var types_1 = require("../types"); +var prow = require("../prow"); +function process(scope) { + return scope.task().then(function (result) { + clearTimeout(scope.timeoutId); + scope.resolve(result); + }).catch(function (reason) { + 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(function () { return process(scope); }); + } + else { + process(scope); + } + } + }); +} +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 = []; + 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 new file mode 100644 index 0000000..27b5a44 --- /dev/null +++ b/lib/functions/timeout.d.ts @@ -0,0 +1,2 @@ +import { ITask } from "../types"; +export declare function timeout(task: ITask, timeout: number): Promise; diff --git a/lib/functions/timeout.js b/lib/functions/timeout.js new file mode 100644 index 0000000..bdc1848 --- /dev/null +++ b/lib/functions/timeout.js @@ -0,0 +1,18 @@ +"use strict"; +var types_1 = require("../types"); +function timeout(task, timeout) { + return new Promise(function (resolve, reject) { + var timeoutId = -1; + if (timeout >= 0) { + timeoutId = setTimeout(reject.bind(null, new types_1.TimeoutError()), timeout); + } + task().then(function (result) { + resolve(result); + clearTimeout(timeoutId); + }, function (reason) { + reject(reason); + clearTimeout(timeoutId); + }); + }); +} +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..49430c2 --- /dev/null +++ b/lib/prow.d.ts @@ -0,0 +1,8 @@ +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"; +export { parallel, queue } from "./functions/parallel"; +export { await } from "./functions/await"; diff --git a/lib/prow.js b/lib/prow.js new file mode 100644 index 0000000..0ca0ade --- /dev/null +++ b/lib/prow.js @@ -0,0 +1,18 @@ +"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; +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 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 f85ef8c..b049481 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,36 @@ { "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", + "pretest": "npm run lint", + "test": "mocha", + "lint": "tslint --project ./", + "prepublish": "npm run build" }, - "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", + "lodash": "^4.16.4", + "mocha": "^3.1.2", + "tslint": "^3.15.1", + "typescript": "^2.0.6" } } diff --git a/src/functions/await.ts b/src/functions/await.ts new file mode 100644 index 0000000..e843d74 --- /dev/null +++ b/src/functions/await.ts @@ -0,0 +1,23 @@ +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 { + return new Promise(function (resolve, reject) { + const conditionHandler = (result) => { + if (result) { + resolve(); + } + }; + prow.retry(condition, -1, delay, timeout).then(conditionHandler).catch(reject); + }); +} \ No newline at end of file diff --git a/src/functions/delay.ts b/src/functions/delay.ts new file mode 100644 index 0000000..c56f2fe --- /dev/null +++ b/src/functions/delay.ts @@ -0,0 +1,11 @@ +/** + * 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); + }); +} \ No newline at end of file diff --git a/src/functions/parallel.ts b/src/functions/parallel.ts new file mode 100644 index 0000000..584d0fc --- /dev/null +++ b/src/functions/parallel.ts @@ -0,0 +1,60 @@ +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 { + process(this); + } +} + +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); + scope.pointer++; + scope.processes++; + } +} + +export function parallel(tasks: Tasks, maxThreads: number = tasks.length): Promise { + if (tasks.length === 0) { + return Promise.resolve(); + } + + 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; + process(scope); + } + }); +} + +export function queue(tasks: Tasks): Promise { + return parallel(tasks, 1); +} \ No newline at end of file diff --git a/src/functions/retry.ts b/src/functions/retry.ts new file mode 100644 index 0000000..fd01793 --- /dev/null +++ b/src/functions/retry.ts @@ -0,0 +1,62 @@ +import { + ITask, TimeoutError +} from "../types"; + +import * as prow from "../prow"; + +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) => { + 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(scope); + } + } + }); +} + +export function retry(task: ITask, times: number = -1, delay: number = 0, timeout: number = -1): Promise { + return new Promise(function (resolve, reject) { + const reasons = []; + 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 new file mode 100644 index 0000000..e1d73d7 --- /dev/null +++ b/src/functions/timeout.ts @@ -0,0 +1,20 @@ +import { + ITask, TimeoutError +} from "../types"; + +export function timeout(task: ITask, timeout: number): Promise { + return new Promise(function (resolve, reject) { + let timeoutId = -1; + if (timeout >= 0) { + timeoutId = setTimeout(reject.bind(null, new TimeoutError()), timeout); + } + + task().then((result) => { + resolve(result); + clearTimeout(timeoutId); + }, (reason) => { + reject(reason); + clearTimeout(timeoutId); + }); + }); +} \ No newline at end of file diff --git a/src/functions/times.ts b/src/functions/times.ts new file mode 100644 index 0000000..80cb344 --- /dev/null +++ b/src/functions/times.ts @@ -0,0 +1,37 @@ +import { + 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([]); + } + + 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/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.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/src/prow.ts b/src/prow.ts new file mode 100644 index 0000000..1e577b2 --- /dev/null +++ b/src/prow.ts @@ -0,0 +1,11 @@ +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"; +export {parallel, queue} from "./functions/parallel"; +export {await} from "./functions/await"; \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..52d8564 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,8 @@ +export interface ITask { + (...args: any[]): Promise; +} + +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..45d6b19 --- /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} = 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, 6000000) + }); + }); + + 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, 6000000) + }); + }); +}); diff --git a/test/parallel.js b/test/parallel.js new file mode 100644 index 0000000..469de35 --- /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 with 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]); + }); +}); diff --git a/test/retry.js b/test/retry.js new file mode 100644 index 0000000..e966a68 --- /dev/null +++ b/test/retry.js @@ -0,0 +1,61 @@ +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 promiseReject(rejectedTimes, resolveValue) { + let counter = 0; + return function () { + if (counter++ < rejectedTimes) { + return Promise.reject(counter); + } + return Promise.resolve(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(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(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 diff --git a/test/timeout.js b/test/timeout.js new file mode 100644 index 0000000..b50a1cb --- /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} = 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(resolvePromise(42), 100), 42); + }); + + it("reject promise value", function () { + return assert.isRejected(prow.timeout(rejectPromise(24), 100), 24); + }); + + it("reject by timeout", function () { + return assert.isRejected(prow.timeout(() => prow.delay(20, 300), 10), 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]); + }); +}); 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/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..2299450 --- /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": [ + "dom", + "es5", + "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