From 9d834e84bd3860ec0bf556437438ba5296e8bb25 Mon Sep 17 00:00:00 2001 From: SGrondin Date: Tue, 14 Jul 2015 10:16:11 -0400 Subject: [PATCH] 1.8.1 Pluggable Promise library --- README.md | 11 ++++++++++- bottleneck.js | 15 +++++++++++++-- bottleneck.min.js | 2 +- bower.json | 2 +- lib/Bottleneck.js | 13 ++++++++++++- package.json | 2 +- scripts/recompile.sh | 2 +- src/Bottleneck.coffee | 3 ++- 8 files changed, 41 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index af30a31..4853bfa 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ It returns `true` if the strategy was executed. ### schedule() -Adds a request to the queue. This is the Promise version of `submit`. It uses the built-in [Promise](http://caniuse.com/#feat=promises) object. +Adds a request to the queue. This is the Promise version of `submit`. It uses the [bluebird](https://github.com/petkaantonov/bluebird) package if available and otherwise uses the built-in [Promise](http://caniuse.com/#feat=promises) object. ```js var fn = function(arg1, arg2, argN) { @@ -94,6 +94,15 @@ limiter.schedule(fn, arg1, arg2, argN); In plain language, `schedule` takes a function fn and a list of arguments. Fn must return a promise. `schedule` returns a promise that will be executed according to the rate limits. It's safe to mix `submit` and `schedule` in the same limiter. +It's also possible to replace the Promise library used. + +```js +var Bottleneck = require("bottleneck"); +Bottleneck.Promise = myPromiseLibrary; + +var limiter = new Bottleneck(maxConcurrent, minTime, highWater, strategy); +``` + #### Gotchas * When using `submit`, if a callback isn't necessary, you must pass `null` or an empty function instead. It will not work if you forget to do this. diff --git a/bottleneck.js b/bottleneck.js index 28a0fa8..9b90147 100644 --- a/bottleneck.js +++ b/bottleneck.js @@ -6,6 +6,8 @@ slice = [].slice; Bottleneck = (function() { + var e; + Bottleneck.strategy = Bottleneck.prototype.strategy = { LEAK: 1, OVERFLOW: 2, @@ -14,6 +16,15 @@ Bottleneck.Cluster = Bottleneck.prototype.Cluster = require("./Cluster"); + Bottleneck.Promise = Bottleneck.prototype.Promise = (function() { + try { + return require("bluebird"); + } catch (_error) { + e = _error; + return Promise; + } + })(); + function Bottleneck(maxNb, minTime, highWater, strategy) { this.maxNb = maxNb != null ? maxNb : 0; this.minTime = minTime != null ? minTime : 0; @@ -122,7 +133,7 @@ return cb.apply({}, Array.prototype.concat.call({}, args)); }); }; - return new Promise((function(_this) { + return new Bottleneck.prototype.Promise((function(_this) { return function(resolve, reject) { return _this.submit.apply({}, Array.prototype.concat.call(wrapped, function() { var args, error; @@ -186,7 +197,7 @@ }).call(this); -},{"./Cluster":2}],2:[function(require,module,exports){ +},{"./Cluster":2,"bluebird":undefined}],2:[function(require,module,exports){ // Generated by CoffeeScript 1.9.2 (function() { var Cluster, diff --git a/bottleneck.min.js b/bottleneck.min.js index e2b3260..c7e98d1 100644 --- a/bottleneck.min.js +++ b/bottleneck.min.js @@ -1 +1 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=Date.now()};Bottleneck.prototype.check=function(){return(this._nbRunning0)};Bottleneck.prototype._tryToRun=function(){var done,index,next,wait;if((this._nbRunning0&&(this.reservoir==null||this.reservoir>0)){this._nbRunning++;if(this.reservoir!=null){this.reservoir--}wait=Math.max(this._nextRequest-Date.now(),0);this._nextRequest=Date.now()+wait+this.minTime;next=this._queue.shift();done=false;index=-1+this._timeouts.push(setTimeout(function(_this){return function(){var completed;completed=function(){var ref;if(!done){done=true;delete _this._timeouts[index];_this._nbRunning--;_this._tryToRun();if(!_this.interrupt){return(ref=next.cb)!=null?ref.apply({},Array.prototype.slice.call(arguments,0)):void 0}}};if(_this.limiter!=null){return _this.limiter.submit.apply(_this.limiter,Array.prototype.concat.call(next.task,next.args,completed))}else{return next.task.apply({},next.args.concat(completed))}}}(this),wait));return true}else{return false}};Bottleneck.prototype.submit=function(){var args,cb,i,reachedHighWaterMark,task;task=arguments[0],args=3<=arguments.length?slice.call(arguments,1,i=arguments.length-1):(i=1,[]),cb=arguments[i++];reachedHighWaterMark=this.highWater>0&&this._queue.length===this.highWater;if(this.strategy===Bottleneck.prototype.strategy.BLOCK&&(reachedHighWaterMark||this.isBlocked())){this._unblockTime=Date.now()+this.penalty;this._nextRequest=this._unblockTime+this.minTime;this._queue=[];return true}else if(reachedHighWaterMark){if(this.strategy===Bottleneck.prototype.strategy.LEAK){this._queue.shift()}else if(this.strategy===Bottleneck.prototype.strategy.OVERFLOW){return reachedHighWaterMark}}this._queue.push({task:task,args:args,cb:cb});this._tryToRun();return reachedHighWaterMark};Bottleneck.prototype.schedule=function(){var args,task,wrapped;task=arguments[0],args=2<=arguments.length?slice.call(arguments,1):[];wrapped=function(cb){return task.apply({},args).then(function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return cb.apply({},Array.prototype.concat.call([],null,args))})["catch"](function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return cb.apply({},Array.prototype.concat.call({},args))})};return new Promise(function(_this){return function(resolve,reject){return _this.submit.apply({},Array.prototype.concat.call(wrapped,function(){var args,error;error=arguments[0],args=2<=arguments.length?slice.call(arguments,1):[];return(error!=null?reject:resolve).apply({},args)}))}}(this))};Bottleneck.prototype.changeSettings=function(maxNb,minTime,highWater,strategy){this.maxNb=maxNb!=null?maxNb:this.maxNb;this.minTime=minTime!=null?minTime:this.minTime;this.highWater=highWater!=null?highWater:this.highWater;this.strategy=strategy!=null?strategy:this.strategy;while(this._tryToRun()){}return this};Bottleneck.prototype.changePenalty=function(penalty){this.penalty=penalty!=null?penalty:this.penalty;return this};Bottleneck.prototype.changeReservoir=function(reservoir){this.reservoir=reservoir;while(this._tryToRun()){}return this};Bottleneck.prototype.incrementReservoir=function(incr){if(incr==null){incr=0}this.changeReservoir(this.reservoir+incr);return this};Bottleneck.prototype.stopAll=function(interrupt){var a,i,len,ref;this.interrupt=interrupt!=null?interrupt:this.interrupt;ref=this._timeouts;for(i=0,len=ref.length;i=Date.now()};Bottleneck.prototype.check=function(){return(this._nbRunning0)};Bottleneck.prototype._tryToRun=function(){var done,index,next,wait;if((this._nbRunning0&&(this.reservoir==null||this.reservoir>0)){this._nbRunning++;if(this.reservoir!=null){this.reservoir--}wait=Math.max(this._nextRequest-Date.now(),0);this._nextRequest=Date.now()+wait+this.minTime;next=this._queue.shift();done=false;index=-1+this._timeouts.push(setTimeout(function(_this){return function(){var completed;completed=function(){var ref;if(!done){done=true;delete _this._timeouts[index];_this._nbRunning--;_this._tryToRun();if(!_this.interrupt){return(ref=next.cb)!=null?ref.apply({},Array.prototype.slice.call(arguments,0)):void 0}}};if(_this.limiter!=null){return _this.limiter.submit.apply(_this.limiter,Array.prototype.concat.call(next.task,next.args,completed))}else{return next.task.apply({},next.args.concat(completed))}}}(this),wait));return true}else{return false}};Bottleneck.prototype.submit=function(){var args,cb,i,reachedHighWaterMark,task;task=arguments[0],args=3<=arguments.length?slice.call(arguments,1,i=arguments.length-1):(i=1,[]),cb=arguments[i++];reachedHighWaterMark=this.highWater>0&&this._queue.length===this.highWater;if(this.strategy===Bottleneck.prototype.strategy.BLOCK&&(reachedHighWaterMark||this.isBlocked())){this._unblockTime=Date.now()+this.penalty;this._nextRequest=this._unblockTime+this.minTime;this._queue=[];return true}else if(reachedHighWaterMark){if(this.strategy===Bottleneck.prototype.strategy.LEAK){this._queue.shift()}else if(this.strategy===Bottleneck.prototype.strategy.OVERFLOW){return reachedHighWaterMark}}this._queue.push({task:task,args:args,cb:cb});this._tryToRun();return reachedHighWaterMark};Bottleneck.prototype.schedule=function(){var args,task,wrapped;task=arguments[0],args=2<=arguments.length?slice.call(arguments,1):[];wrapped=function(cb){return task.apply({},args).then(function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return cb.apply({},Array.prototype.concat.call([],null,args))})["catch"](function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return cb.apply({},Array.prototype.concat.call({},args))})};return new Bottleneck.prototype.Promise(function(_this){return function(resolve,reject){return _this.submit.apply({},Array.prototype.concat.call(wrapped,function(){var args,error;error=arguments[0],args=2<=arguments.length?slice.call(arguments,1):[];return(error!=null?reject:resolve).apply({},args)}))}}(this))};Bottleneck.prototype.changeSettings=function(maxNb,minTime,highWater,strategy){this.maxNb=maxNb!=null?maxNb:this.maxNb;this.minTime=minTime!=null?minTime:this.minTime;this.highWater=highWater!=null?highWater:this.highWater;this.strategy=strategy!=null?strategy:this.strategy;while(this._tryToRun()){}return this};Bottleneck.prototype.changePenalty=function(penalty){this.penalty=penalty!=null?penalty:this.penalty;return this};Bottleneck.prototype.changeReservoir=function(reservoir){this.reservoir=reservoir;while(this._tryToRun()){}return this};Bottleneck.prototype.incrementReservoir=function(incr){if(incr==null){incr=0}this.changeReservoir(this.reservoir+incr);return this};Bottleneck.prototype.stopAll=function(interrupt){var a,i,len,ref;this.interrupt=interrupt!=null?interrupt:this.interrupt;ref=this._timeouts;for(i=0,len=ref.length;i" diff --git a/lib/Bottleneck.js b/lib/Bottleneck.js index 26e862d..03506db 100644 --- a/lib/Bottleneck.js +++ b/lib/Bottleneck.js @@ -5,6 +5,8 @@ slice = [].slice; Bottleneck = (function() { + var e; + Bottleneck.strategy = Bottleneck.prototype.strategy = { LEAK: 1, OVERFLOW: 2, @@ -13,6 +15,15 @@ Bottleneck.Cluster = Bottleneck.prototype.Cluster = require("./Cluster"); + Bottleneck.Promise = Bottleneck.prototype.Promise = (function() { + try { + return require("bluebird"); + } catch (_error) { + e = _error; + return Promise; + } + })(); + function Bottleneck(maxNb, minTime, highWater, strategy) { this.maxNb = maxNb != null ? maxNb : 0; this.minTime = minTime != null ? minTime : 0; @@ -121,7 +132,7 @@ return cb.apply({}, Array.prototype.concat.call({}, args)); }); }; - return new Promise((function(_this) { + return new Bottleneck.prototype.Promise((function(_this) { return function(resolve, reject) { return _this.submit.apply({}, Array.prototype.concat.call(wrapped, function() { var args, error; diff --git a/package.json b/package.json index e944b2d..4674b2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bottleneck", - "version": "1.8.0", + "version": "1.8.1", "description": "Async rate limiter", "main": "lib/index.js", "scripts": { diff --git a/scripts/recompile.sh b/scripts/recompile.sh index 5c28b62..2a549c9 100755 --- a/scripts/recompile.sh +++ b/scripts/recompile.sh @@ -11,7 +11,7 @@ echo 'Compiling bottleneck...' node_modules/coffee-script/bin/coffee -c src/*.coffee rm lib/*.js mv src/*.js lib/ -node_modules/browserify/bin/cmd.js lib/index.js > bottleneck.js +node_modules/browserify/bin/cmd.js -u bluebird lib/index.js > bottleneck.js node_modules/uglify-js/bin/uglifyjs bottleneck.js -o bottleneck.min.js echo 'Done!' diff --git a/src/Bottleneck.coffee b/src/Bottleneck.coffee index bbfe903..4213a3f 100644 --- a/src/Bottleneck.coffee +++ b/src/Bottleneck.coffee @@ -1,6 +1,7 @@ class Bottleneck Bottleneck.strategy = Bottleneck::strategy = {LEAK:1, OVERFLOW:2, BLOCK:3} Bottleneck.Cluster = Bottleneck::Cluster = require "./Cluster" + Bottleneck.Promise = Bottleneck::Promise = try require "bluebird" catch e then Promise constructor: (@maxNb=0, @minTime=0, @highWater=0, @strategy=Bottleneck::strategy.LEAK) -> @_nextRequest = Date.now() @_nbRunning = 0 @@ -53,7 +54,7 @@ class Bottleneck (task.apply {}, args) .then (args...) -> cb.apply {}, Array::concat.call [], null, args .catch (args...) -> cb.apply {}, Array::concat.call {}, args - new Promise (resolve, reject) => + new Bottleneck::Promise (resolve, reject) => @submit.apply {}, Array::concat.call wrapped, (error, args...) -> (if error? then reject else resolve).apply {}, args changeSettings: (@maxNb=@maxNb, @minTime=@minTime, @highWater=@highWater, @strategy=@strategy) ->