diff --git a/lib/concurrent/index.js b/lib/concurrent/index.js index cf0e7b9..88c92c5 100644 --- a/lib/concurrent/index.js +++ b/lib/concurrent/index.js @@ -6,5 +6,7 @@ module.exports = { TimeoutException: timeout.TimeoutException, TaskQueue: TaskQueue.TaskQueue, RejectionException: TaskQueue.RejectionException, - timeout: timeout.timeout + timeout: timeout.timeout, + delay: timeout.delay, + throttle: timeout.throttle } diff --git a/lib/concurrent/timeout.js b/lib/concurrent/timeout.js index bd0006e..58ce969 100644 --- a/lib/concurrent/timeout.js +++ b/lib/concurrent/timeout.js @@ -30,7 +30,47 @@ function timeout (promise, timeout, message) { }) } +/** + * Delays processing of callback for specified time. If no callback is + * given, returns empty promise that will resolve in specified time + * + * @param {int} time + * @param {Function} [callback] + * + * @return {Thenable.<*>} + */ +function delay (time, callback) { + callback = callback || function () {} + return new Promise(function (resolve, reject) { + setTimeout(function () { + try { + resolve(callback()) + } catch (e) { + reject(e) + } + }, time) + }) +} + +/** + * Wraps given promise with another one which won't resolve earlier + * than specified time. + * + * @param {Thenable.<*>} promise + * @param {int} time Throttle time in milliseconds + * @return {Thenable.<*>} + */ +function throttle (promise, time) { + return new Promise(function (resolve) { + setTimeout(resolve, time) + }).then(function () { + return promise + }) +} + module.exports = { timeout: timeout, + delay: delay, + throttle: throttle, TimeoutException: TimeoutException } diff --git a/package.json b/package.json index 04f080b..b2056f5 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,7 @@ "npm-cli-login": "0.0.10", "npm-utils": "^1.11.0", "promise": "^7.1.1", - "sinon": "^1.17.6", - "sinon-as-promised": "^4.0.2", + "sinon": "^3.2.0", "standard": "^9.0.0", "uglify-js": "^3.0.27" }, diff --git a/test/suites/integration/concurrent/timeout.delay.spec.js b/test/suites/integration/concurrent/timeout.delay.spec.js new file mode 100644 index 0000000..e27023a --- /dev/null +++ b/test/suites/integration/concurrent/timeout.delay.spec.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ + +var Sinon = require('sinon') +var Chai = require('chai') +var expect = Chai.expect +var delay = require('../../../../lib').Concurrent.delay + +describe('Integration', function () { + describe('/concurrent', function () { + describe('/timeout.js', function () { + describe('.delay', function () { + var clock + + beforeEach(function () { + clock = Sinon.useFakeTimers() + }) + + afterEach(function () { + clock.restore() + }) + + it('delays processing of specified code', function () { + var callback = Sinon.stub() + var delayed = delay(10, callback) + expect(callback.callCount).to.eq(0) + clock.next() + return delayed + .then(function () { + expect(callback.callCount).to.eq(1) + }) + }) + + it('creates delayed promise if no callback is specified', function () { + var promise = delay(1) + clock.next() + return promise + }) + }) + }) + }) +}) diff --git a/test/suites/integration/concurrent/timeout.throttle.spec.js b/test/suites/integration/concurrent/timeout.throttle.spec.js new file mode 100644 index 0000000..10d63c9 --- /dev/null +++ b/test/suites/integration/concurrent/timeout.throttle.spec.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ + +var Sinon = require('sinon') +var Chai = require('chai') +var expect = Chai.expect +var throttle = require('../../../../lib').Concurrent.throttle + +describe('Integration', function () { + describe('/concurrent', function () { + describe('/timeout.js', function () { + describe('.throttle', function () { + var clock + + beforeEach(function () { + clock = Sinon.useFakeTimers() + }) + + afterEach(function () { + clock.restore() + }) + + it('creates promise that resolves after passed time if it resolved too fast', function () { + var promise = Promise.resolve() + var stub = Sinon.stub() + var throttled = throttle(promise, 10).then(stub) + expect(stub.callCount).to.eq(0) + clock.next() + return throttled + .then(function () { + expect(stub.callCount).to.eq(1) + }) + }) + + it('doesn\'t slow down promise that takes longer that throttle time', function () { + var promise = new Promise(function (resolve) { + setTimeout(resolve, 30) + }) + var stub = Sinon.stub() + var throttled = throttle(promise, 10).then(stub) + expect(stub.callCount).to.eq(0) + clock.tick(30) + return throttled + .then(function () { + expect(stub.callCount).to.eq(1) + }) + }) + }) + }) + }) +}) diff --git a/test/suites/integration/concurrent/timeout.timeout.spec.js b/test/suites/integration/concurrent/timeout.timeout.spec.js index a439f13..c0fff26 100644 --- a/test/suites/integration/concurrent/timeout.timeout.spec.js +++ b/test/suites/integration/concurrent/timeout.timeout.spec.js @@ -3,6 +3,7 @@ var Concurrent = require('../../../../lib').Concurrent var timeout = Concurrent.timeout var TimeoutException = Concurrent.TimeoutException +var Sinon = require('sinon') var Chai = require('chai') var expect = Chai.expect @@ -16,6 +17,16 @@ describe('Integration', function () { describe('/concurrent', function () { describe('/timeout.js', function () { describe('.timeout', function () { + var clock + + beforeEach(function () { + clock = Sinon.useFakeTimers() + }) + + afterEach(function () { + clock.restore() + }) + it('returns promise if timeout is negative', function () { var promise = new Promise(function () {}) return expect(timeout(promise, -1)).to.equal(promise) @@ -34,6 +45,7 @@ describe('Integration', function () { it('wraps promise in timed out one', function () { var promise = new Promise(function () {}) var wrapped = timeout(promise, 0) + clock.next() return expect(wrapped).to.eventually.be.rejectedWith(TimeoutException) }) @@ -41,6 +53,7 @@ describe('Integration', function () { var message = 'foo' var promise = new Promise(function () {}) var wrapped = timeout(promise, 0, message) + clock.next() return wrapped .then(branchStopper, function (error) { expect(error).to.be.instanceOf(TimeoutException) @@ -55,6 +68,7 @@ describe('Integration', function () { .then(function () { return timeout(promise, 0) }) + clock.next() return expect(wrapped).to.eventually.eq(value) }) })