pax_global_header00006660000000000000000000000064130574045770014526gustar00rootroot0000000000000052 comment=2b05fe72f0cf33a2aac951cd68dd949fccb0d9e9 call-limit-1.1.0/000077500000000000000000000000001305740457700135545ustar00rootroot00000000000000call-limit-1.1.0/.gitignore000066400000000000000000000000201305740457700155340ustar00rootroot00000000000000*~ node_modules call-limit-1.1.0/.npmignore000066400000000000000000000000031305740457700155440ustar00rootroot00000000000000*~ call-limit-1.1.0/README.md000066400000000000000000000041621305740457700150360ustar00rootroot00000000000000call-limit ---------- Limit the number of simultaneous executions of a async function. ```javascript var fs = require('fs') var limit = require('call-limit') var limitedStat = limit(fs.stat, 5) ``` Or with promise returning functions: ```javascript var fs = Bluebird.promisifyAll(require('fs')) var limit = require('call-limit') var limitedStat = limit.promise(fs.statAsync, 5) ``` ### USAGE: Given that: ```javascript var limit = require('call-limit') ``` ### limit(func, maxRunning) → limitedFunc The returned function will execute up to maxRunning calls of `func` at once. Beyond that they get queued and called when the previous call completes. `func` must accept a callback as the final argument and must call it when it completes, or `call-limit` won't know to dequeue the next thing to run. By contrast, callers to `limitedFunc` do NOT have to pass in a callback, but if they do they'll be called when `func` calls its callback. ### limit.promise(func, maxRunning) → limitedFunc The returned function will execute up to maxRunning calls of `func` at once. Beyond that they get queued and called when the previous call completes. `func` must return a promise. `limitedFunc` will return a promise that resolves with the promise returned from the call to `func`. ### limit.method(class, methodName, maxRunning) This is sugar for: ```javascript class.prototype.methodName = limit(class.prototype.methodName, maxRunning) ``` ### limit.method(object, methodName, maxRunning) This is sugar for: ```javascript object.methodName = limit(object.methodName, maxRunning) ``` For example `limit.promise.method(fs, 'stat', 5)` is the same as `fs.stat = limit.promise(fs.stat, 5)`. ### limit.promise.method(class, methodName, maxRunning) This is sugar for: ```javascript class.prototype.methodName = limit.promise(class.prototype.methodName, maxRunning) ``` ### limit.promise.method(object, methodName, maxRunning) This is sugar for: ```javascript object.methodName = limit.promise(object.methodName, maxRunning) ``` For example `limit.promise.method(fs, 'statAsync', 5)` is the same as `fs.statAsync = limit.promise(fs.statAsync, 5)`. call-limit-1.1.0/call-limit.js000066400000000000000000000044221305740457700161430ustar00rootroot00000000000000"use strict" var defaultMaxRunning = 50 var limit = module.exports = function (func, maxRunning) { var running = 0 var queue = [] if (!maxRunning) maxRunning = defaultMaxRunning return function limited () { var self = this var args = Array.prototype.slice.call(arguments) if (running >= maxRunning) { queue.push({self: this, args: args}) return } var cb = typeof args[args.length-1] === 'function' && args.pop() ++ running args.push(function () { var cbargs = arguments -- running cb && process.nextTick(function () { cb.apply(self, cbargs) }) if (queue.length) { var next = queue.shift() limited.apply(next.self, next.args) } }) func.apply(self, args) } } module.exports.method = function (classOrObj, method, maxRunning) { if (typeof classOrObj === 'function') { var func = classOrObj.prototype[method] classOrObj.prototype[method] = limit(func, maxRunning) } else { var func = classOrObj[method] classOrObj[method] = limit(func, maxRunning) } } module.exports.promise = function (func, maxRunning) { var running = 0 var queue = [] if (!maxRunning) maxRunning = defaultMaxRunning return function () { var self = this var args = Array.prototype.slice.call(arguments) return new Promise(function (resolve) { if (running >= maxRunning) { queue.push({self: self, args: args, resolve: resolve}) return } else { runNext(self, args, resolve) } function runNext (self, args, resolve) { ++ running resolve( func.apply(self, args) .then(finish, function (err) { finish(err) throw err })) } function finish () { -- running if (queue.length) { var next = queue.shift() process.nextTick(runNext, next.self, next.args, next.resolve) } } }) } } module.exports.promise.method = function (classOrObj, method, maxRunning) { if (typeof classOrObj === 'function') { var func = classOrObj.prototype[method] classOrObj.prototype[method] = limit.promise(func, maxRunning) } else { var func = classOrObj[method] classOrObj[method] = limit.promise(func, maxRunning) } } call-limit-1.1.0/package.json000066400000000000000000000011271305740457700160430ustar00rootroot00000000000000{ "name": "call-limit", "version": "1.1.0", "description": "Limit the number of simultaneous calls to an async function", "main": "call-limit.js", "scripts": { "test": "tap test/*.js" }, "repository": { "type": "git", "url": "https://github.com/iarna/call-limit" }, "author": "Rebecca Turner ", "license": "ISC", "bugs": { "url": "https://github.com/iarna/call-limit/issues" }, "homepage": "https://npmjs.com/packages/call-limit", "dependencies": {}, "devDependencies": { "tap": "^1.0.0" }, "files": [ "call-limit.js" ] } call-limit-1.1.0/test/000077500000000000000000000000001305740457700145335ustar00rootroot00000000000000call-limit-1.1.0/test/limit.js000066400000000000000000000071111305740457700162070ustar00rootroot00000000000000'use strict' var test = require('tap').test var limit = require('../call-limit.js') test('limit', function (t) { t.plan(5) var called = 0 var completed = 0 var finishers = [] var limited = limit(function (num, cb) { ++ called finishers.push(cb) }, 2) ;[1,2,3].forEach(function (num) { limited(num, function () { ++ completed }) }) setImmediate(function () { t.is(called, 2, 'Immediately queued 2 callbacks') t.is(completed, 0, 'With no completion yet') // immediately complete one of them... finishers.shift()() setImmediate(afterCompletion) }) function afterCompletion () { t.is(completed, 1, 'Calling the finisher completed the first one') t.is(called, 3,'Third action was started') finishers.shift()() finishers.shift()() setImmediate(afterAllDone) } function afterAllDone () { t.is(completed, 3, 'All completed') } }) test('limit-obj-method', function (t) { t.plan(5) var called = 0 var completed = 0 var finishers = [] var example = { test: function (num, cb) { ++ called finishers.push(cb) } } limit.method(example, 'test', 2) ;[1,2,3].forEach(function (num) { example.test(num, function () { ++ completed }) }) setImmediate(function () { t.is(called, 2, 'Immediately queued 2 callbacks') t.is(completed, 0, 'With no completion yet') // immediately complete one of them... finishers.shift()() setImmediate(afterCompletion) }) function afterCompletion () { t.is(completed, 1, 'Calling the finisher completed the first one') t.is(called, 3,'Third action was started') finishers.shift()() finishers.shift()() setImmediate(afterAllDone) } function afterAllDone () { t.is(completed, 3, 'All completed') } }) test('limit-class-method', function (t) { t.plan(5) var called = 0 var completed = 0 var finishers = [] function Example () {} Example.prototype = { test: function (num, cb) { ++ called finishers.push(cb) } } limit.method(Example, 'test', 2) var example = new Example() ;[1,2,3].forEach(function (num) { example.test(num, function () { ++ completed }) }) setImmediate(function () { t.is(called, 2, 'Immediately queued 2 callbacks') t.is(completed, 0, 'With no completion yet') // immediately complete one of them... finishers.shift()() setImmediate(afterCompletion) }) function afterCompletion () { t.is(completed, 1, 'Calling the finisher completed the first one') t.is(called, 3,'Third action was started') finishers.shift()() finishers.shift()() setImmediate(afterAllDone) } function afterAllDone () { t.is(completed, 3, 'All completed') } }) test('promise', function (t) { if (!global.Promise) return t.plan(5) var called = 0 var completed = 0 var finishers = [] var limited = limit.promise(function (num) { ++ called return new Promise(function (resolve) { finishers.push(resolve) }) }, 2) for (var ii = 0; ii < 3; ++ii) { limited(ii + 1).then(function () { ++ completed }) } setImmediate(function () { t.is(called, 2, 'Immediately queued 2 callbacks') t.is(completed, 0, 'With no completion yet') // immediately complete one of them... finishers.shift()() setImmediate(afterCompletion) }) function afterCompletion () { t.is(completed, 1, 'Calling the finisher completed the first one') t.is(called, 3,'Third action was started') finishers.shift()() finishers.shift()() setImmediate(afterAllDone) } function afterAllDone () { t.is(completed, 3, 'All completed') } })