From e85979dc44c50ee7b7077e33978ed5a60f814fc9 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 5 Oct 2016 13:02:58 -0400 Subject: [PATCH] 1.14.0 'idle' event, nbRunning(), removeAllListeners() --- README.md | 17 +++++- bottleneck.js | 27 ++++++++- bottleneck.min.js | 2 +- bower.json | 2 +- lib/Bottleneck.js | 27 ++++++++- package.json | 2 +- src/Bottleneck.coffee | 9 ++- test/general.js | 132 +++++++++++++++++++++++++++++------------- 8 files changed, 169 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 12a49aa..bc9aa9a 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,13 @@ limiter.nbQueued(priority); `priority` is optional. Without that argument, it'll return the total number of requests waiting to be executed, otherwise it'll only count the number of requests with that specific priority. +### nbRunning() + +```js +limiter.nbRunning(); +``` + +Returns the number of requests currently running in the limiter. ### check() @@ -202,7 +209,7 @@ Cancels all *queued up* requests and prevents additonal requests from being adde ### Events -Event names: `empty`, `dropped`. +Event names: `empty`, `idle`, `dropped`. ```js limiter.on('empty', function () { @@ -210,6 +217,12 @@ limiter.on('empty', function () { }) ``` +```js +limiter.on('idle', function () { + // This will be called when the nbQueued() drops to 0 AND there is nothing currently running in the limiter. +}) +``` + ```js limiter.on('dropped', function (dropped) { // This will be called when a strategy was triggered. @@ -217,6 +230,8 @@ limiter.on('dropped', function (dropped) { }) ``` +Use `removeAllListeners()` with an optional event name as first argument to remove listeners. + **Note:** It's possible to set multiple callbacks to the same event name. diff --git a/bottleneck.js b/bottleneck.js index 89f5c5a..f2cb4fb 100644 --- a/bottleneck.js +++ b/bottleneck.js @@ -120,6 +120,10 @@ } }; + Bottleneck.prototype.nbRunning = function() { + return this._nbRunning; + }; + Bottleneck.prototype._getFirst = function(arr) { return this._find(arr, function(x) { return x.length > 0; @@ -127,7 +131,7 @@ }; Bottleneck.prototype._conditionsCheck = function() { - return (this._nbRunning < this.maxNb || this.maxNb <= 0) && ((this.reservoir == null) || this.reservoir > 0); + return (this.nbRunning() < this.maxNb || this.maxNb <= 0) && ((this.reservoir == null) || this.reservoir > 0); }; Bottleneck.prototype.check = function() { @@ -159,6 +163,9 @@ delete _this._running[index]; _this._nbRunning--; _this._tryToRun(); + if (_this.nbRunning() === 0 && _this.nbQueued() === 0) { + _this._trigger("idle", []); + } if (!_this.interrupt) { return (ref = next.cb) != null ? ref.apply({}, Array.prototype.slice.call(arguments, 0)) : void 0; } @@ -284,6 +291,18 @@ return this; }; + Bottleneck.prototype.removeAllListeners = function(name) { + if (name == null) { + name = null; + } + if (name != null) { + delete this.events[name]; + } else { + this.events = {}; + } + return this; + }; + Bottleneck.prototype.stopAll = function(interrupt) { var a, j, job, k, len, len1, ref, ref1; this.interrupt = interrupt != null ? interrupt : this.interrupt; @@ -306,7 +325,11 @@ while (job = (this._getFirst(this._queues)).shift()) { this._trigger("dropped", [job]); } - return this._trigger("empty", []); + this._trigger("empty", []); + if (this.nbRunning() === 0) { + this._trigger("idle", []); + } + return this; }; return Bottleneck; diff --git a/bottleneck.min.js b/bottleneck.min.js index 889a00a..83aa8b7 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=ref;i=1<=ref?++j:--j){results.push(new Bottleneck.prototype.DLList)}return results};Bottleneck.prototype.chain=function(limiter){this.limiter=limiter;return this};Bottleneck.prototype.isBlocked=function(){return this._unblockTime>=Date.now()};Bottleneck.prototype._sanitizePriority=function(priority){var sProperty;sProperty=~~priority!==priority?MIDDLE_PRIORITY:priority;if(sProperty<0){return 0}else if(sProperty>NB_PRIORITIES-1){return NB_PRIORITIES-1}else{return sProperty}};Bottleneck.prototype._find=function(arr,fn){var i,j,len,x;for(i=j=0,len=arr.length;j0})};Bottleneck.prototype._conditionsCheck=function(){return(this._nbRunning0)};Bottleneck.prototype.check=function(){return this._conditionsCheck()&&this._nextRequest-Date.now()<=0};Bottleneck.prototype._tryToRun=function(){var done,index,next,queued,wait;if(this._conditionsCheck()&&(queued=this.nbQueued())>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._getFirst(this._queues).shift();if(queued===1){this._trigger("empty",[])}done=false;index=-1+this._running.push({timeout:setTimeout(function(_this){return function(){var completed;completed=function(){var ref;if(!done){done=true;delete _this._running[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(next.task,next.args,completed))}else{return next.task.apply({},next.args.concat(completed))}}}(this),wait),job:next});return true}else{return false}};Bottleneck.prototype.submit=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return this.submitPriority.apply({},Array.prototype.concat(MIDDLE_PRIORITY,args))};Bottleneck.prototype.submitPriority=function(){var args,cb,j,job,priority,reachedHighWaterMark,shifted,task;priority=arguments[0],task=arguments[1],args=4<=arguments.length?slice.call(arguments,2,j=arguments.length-1):(j=2,[]),cb=arguments[j++];job={task:task,args:args,cb:cb};priority=this._sanitizePriority(priority);reachedHighWaterMark=this.highWater>=0&&this.nbQueued()===this.highWater&&!this.check();if(this.strategy===Bottleneck.prototype.strategy.BLOCK&&(reachedHighWaterMark||this.isBlocked())){this._unblockTime=Date.now()+this.penalty;this._nextRequest=this._unblockTime+this.minTime;this._queues=this._makeQueues();this._trigger("dropped",[job]);return true}else if(reachedHighWaterMark){shifted=this.strategy===Bottleneck.prototype.strategy.LEAK?this._getFirst(this._queues.slice(priority).reverse()).shift():this.strategy===Bottleneck.prototype.strategy.OVERFLOW_PRIORITY?this._getFirst(this._queues.slice(priority+1).reverse()).shift():this.strategy===Bottleneck.prototype.strategy.OVERFLOW?job:void 0;if(shifted!=null){this._trigger("dropped",[shifted])}if(shifted==null||this.strategy===Bottleneck.prototype.strategy.OVERFLOW){return reachedHighWaterMark}}this._queues[priority].push(job);this._tryToRun();return reachedHighWaterMark};Bottleneck.prototype.schedule=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return this.schedulePriority.apply({},Array.prototype.concat(MIDDLE_PRIORITY,args))};Bottleneck.prototype.schedulePriority=function(){var args,priority,task,wrapped;priority=arguments[0],task=arguments[1],args=3<=arguments.length?slice.call(arguments,2):[];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(null,args))})["catch"](function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return cb.apply({},args)})};return new Bottleneck.prototype.Promise(function(_this){return function(resolve,reject){return _this.submitPriority.apply({},Array.prototype.concat(priority,wrapped,function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return(args[0]!=null?reject:(args.shift(),resolve)).apply({},args)}))}}(this))};Bottleneck.prototype.changeSettings=function(maxNb,minTime,highWater,strategy,rejectOnDrop){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;this.rejectOnDrop=rejectOnDrop!=null?rejectOnDrop:this.rejectOnDrop;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.on=function(name,cb){if(this.events[name]!=null){this.events[name].push(cb)}else{this.events[name]=[cb]}return this};Bottleneck.prototype.stopAll=function(interrupt){var a,j,job,k,len,len1,ref,ref1;this.interrupt=interrupt!=null?interrupt:this.interrupt;ref=this._running;for(j=0,len=ref.length;j=ref;i=1<=ref?++j:--j){results.push(new Bottleneck.prototype.DLList)}return results};Bottleneck.prototype.chain=function(limiter){this.limiter=limiter;return this};Bottleneck.prototype.isBlocked=function(){return this._unblockTime>=Date.now()};Bottleneck.prototype._sanitizePriority=function(priority){var sProperty;sProperty=~~priority!==priority?MIDDLE_PRIORITY:priority;if(sProperty<0){return 0}else if(sProperty>NB_PRIORITIES-1){return NB_PRIORITIES-1}else{return sProperty}};Bottleneck.prototype._find=function(arr,fn){var i,j,len,x;for(i=j=0,len=arr.length;j0})};Bottleneck.prototype._conditionsCheck=function(){return(this.nbRunning()0)};Bottleneck.prototype.check=function(){return this._conditionsCheck()&&this._nextRequest-Date.now()<=0};Bottleneck.prototype._tryToRun=function(){var done,index,next,queued,wait;if(this._conditionsCheck()&&(queued=this.nbQueued())>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._getFirst(this._queues).shift();if(queued===1){this._trigger("empty",[])}done=false;index=-1+this._running.push({timeout:setTimeout(function(_this){return function(){var completed;completed=function(){var ref;if(!done){done=true;delete _this._running[index];_this._nbRunning--;_this._tryToRun();if(_this.nbRunning()===0&&_this.nbQueued()===0){_this._trigger("idle",[])}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(next.task,next.args,completed))}else{return next.task.apply({},next.args.concat(completed))}}}(this),wait),job:next});return true}else{return false}};Bottleneck.prototype.submit=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return this.submitPriority.apply({},Array.prototype.concat(MIDDLE_PRIORITY,args))};Bottleneck.prototype.submitPriority=function(){var args,cb,j,job,priority,reachedHighWaterMark,shifted,task;priority=arguments[0],task=arguments[1],args=4<=arguments.length?slice.call(arguments,2,j=arguments.length-1):(j=2,[]),cb=arguments[j++];job={task:task,args:args,cb:cb};priority=this._sanitizePriority(priority);reachedHighWaterMark=this.highWater>=0&&this.nbQueued()===this.highWater&&!this.check();if(this.strategy===Bottleneck.prototype.strategy.BLOCK&&(reachedHighWaterMark||this.isBlocked())){this._unblockTime=Date.now()+this.penalty;this._nextRequest=this._unblockTime+this.minTime;this._queues=this._makeQueues();this._trigger("dropped",[job]);return true}else if(reachedHighWaterMark){shifted=this.strategy===Bottleneck.prototype.strategy.LEAK?this._getFirst(this._queues.slice(priority).reverse()).shift():this.strategy===Bottleneck.prototype.strategy.OVERFLOW_PRIORITY?this._getFirst(this._queues.slice(priority+1).reverse()).shift():this.strategy===Bottleneck.prototype.strategy.OVERFLOW?job:void 0;if(shifted!=null){this._trigger("dropped",[shifted])}if(shifted==null||this.strategy===Bottleneck.prototype.strategy.OVERFLOW){return reachedHighWaterMark}}this._queues[priority].push(job);this._tryToRun();return reachedHighWaterMark};Bottleneck.prototype.schedule=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return this.schedulePriority.apply({},Array.prototype.concat(MIDDLE_PRIORITY,args))};Bottleneck.prototype.schedulePriority=function(){var args,priority,task,wrapped;priority=arguments[0],task=arguments[1],args=3<=arguments.length?slice.call(arguments,2):[];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(null,args))})["catch"](function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return cb.apply({},args)})};return new Bottleneck.prototype.Promise(function(_this){return function(resolve,reject){return _this.submitPriority.apply({},Array.prototype.concat(priority,wrapped,function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];return(args[0]!=null?reject:(args.shift(),resolve)).apply({},args)}))}}(this))};Bottleneck.prototype.changeSettings=function(maxNb,minTime,highWater,strategy,rejectOnDrop){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;this.rejectOnDrop=rejectOnDrop!=null?rejectOnDrop:this.rejectOnDrop;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.on=function(name,cb){if(this.events[name]!=null){this.events[name].push(cb)}else{this.events[name]=[cb]}return this};Bottleneck.prototype.removeAllListeners=function(name){if(name==null){name=null}if(name!=null){delete this.events[name]}else{this.events={}}return this};Bottleneck.prototype.stopAll=function(interrupt){var a,j,job,k,len,len1,ref,ref1;this.interrupt=interrupt!=null?interrupt:this.interrupt;ref=this._running;for(j=0,len=ref.length;j" diff --git a/lib/Bottleneck.js b/lib/Bottleneck.js index 66d6bd6..66615f6 100644 --- a/lib/Bottleneck.js +++ b/lib/Bottleneck.js @@ -119,6 +119,10 @@ } }; + Bottleneck.prototype.nbRunning = function() { + return this._nbRunning; + }; + Bottleneck.prototype._getFirst = function(arr) { return this._find(arr, function(x) { return x.length > 0; @@ -126,7 +130,7 @@ }; Bottleneck.prototype._conditionsCheck = function() { - return (this._nbRunning < this.maxNb || this.maxNb <= 0) && ((this.reservoir == null) || this.reservoir > 0); + return (this.nbRunning() < this.maxNb || this.maxNb <= 0) && ((this.reservoir == null) || this.reservoir > 0); }; Bottleneck.prototype.check = function() { @@ -158,6 +162,9 @@ delete _this._running[index]; _this._nbRunning--; _this._tryToRun(); + if (_this.nbRunning() === 0 && _this.nbQueued() === 0) { + _this._trigger("idle", []); + } if (!_this.interrupt) { return (ref = next.cb) != null ? ref.apply({}, Array.prototype.slice.call(arguments, 0)) : void 0; } @@ -283,6 +290,18 @@ return this; }; + Bottleneck.prototype.removeAllListeners = function(name) { + if (name == null) { + name = null; + } + if (name != null) { + delete this.events[name]; + } else { + this.events = {}; + } + return this; + }; + Bottleneck.prototype.stopAll = function(interrupt) { var a, j, job, k, len, len1, ref, ref1; this.interrupt = interrupt != null ? interrupt : this.interrupt; @@ -305,7 +324,11 @@ while (job = (this._getFirst(this._queues)).shift()) { this._trigger("dropped", [job]); } - return this._trigger("empty", []); + this._trigger("empty", []); + if (this.nbRunning() === 0) { + this._trigger("idle", []); + } + return this; }; return Bottleneck; diff --git a/package.json b/package.json index fc419bf..d9f2e72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bottleneck", - "version": "1.13.1", + "version": "1.14.0", "description": "Async rate limiter", "main": "lib/index.js", "scripts": { diff --git a/src/Bottleneck.coffee b/src/Bottleneck.coffee index 159b5b9..027237f 100644 --- a/src/Bottleneck.coffee +++ b/src/Bottleneck.coffee @@ -28,8 +28,9 @@ class Bottleneck if sProperty < 0 then 0 else if sProperty > NB_PRIORITIES-1 then NB_PRIORITIES-1 else sProperty _find: (arr, fn) -> (for x, i in arr then if fn x then return x); [] nbQueued: (priority) -> if priority? then @_queues[@_sanitizePriority priority].length else @_queues.reduce ((a, b) -> a+b.length), 0 + nbRunning: () -> @_nbRunning _getFirst: (arr) -> @_find arr, (x) -> x.length > 0 - _conditionsCheck: -> (@_nbRunning < @maxNb or @maxNb <= 0) and (not @reservoir? or @reservoir > 0) + _conditionsCheck: -> (@nbRunning() < @maxNb or @maxNb <= 0) and (not @reservoir? or @reservoir > 0) check: -> @_conditionsCheck() and (@_nextRequest-Date.now()) <= 0 _tryToRun: -> if @_conditionsCheck() and (queued = @nbQueued()) > 0 @@ -48,6 +49,7 @@ class Bottleneck delete @_running[index] @_nbRunning-- @_tryToRun() + if @nbRunning() == 0 and @nbQueued() == 0 then @_trigger "idle", [] if not @interrupt then next.cb?.apply {}, Array::slice.call arguments, 0 if @limiter? then @limiter.submit.apply @limiter, Array::concat next.task, next.args, completed else next.task.apply {}, next.args.concat completed @@ -97,6 +99,9 @@ class Bottleneck on: (name, cb) -> if @events[name]? then @events[name].push cb else @events[name] = [cb] @ + removeAllListeners: (name=null) -> + if name? then delete @events[name] else @events = {} + @ stopAll: (@interrupt=@interrupt) -> (clearTimeout a.timeout for a in @_running) @_tryToRun = -> @@ -104,5 +109,7 @@ class Bottleneck if @interrupt then (@_trigger "dropped", [a.job] for a in @_running) while job = (@_getFirst @_queues).shift() then @_trigger "dropped", [job] @_trigger "empty", [] + if @nbRunning() == 0 then @_trigger "idle", [] + @ module.exports = Bottleneck diff --git a/test/general.js b/test/general.js index 8ebd6f4..7a2e8e9 100644 --- a/test/general.js +++ b/test/general.js @@ -1,69 +1,95 @@ describe('General', function () { - describe('nbQueued', function () { - it('Should return the nbQueued with and without a priority value', function (done) { - var c = makeTest(1, 250) + it('Should return the nbQueued with and without a priority value', function (done) { + var c = makeTest(1, 250) - console.assert(c.limiter.nbQueued() === 0) + console.assert(c.limiter.nbQueued() === 0) - c.limiter.submit(c.job, null, 1, c.noErrVal(1)) - console.assert(c.limiter.nbQueued() === 0) // It's already running + c.limiter.submit(c.job, null, 1, c.noErrVal(1)) + console.assert(c.limiter.nbQueued() === 0) // It's already running - c.limiter.submit(c.job, null, 2, c.noErrVal(2)) - console.assert(c.limiter.nbQueued() === 1) - console.assert(c.limiter.nbQueued(1) === 0) - console.assert(c.limiter.nbQueued(5) === 1) + c.limiter.submit(c.job, null, 2, c.noErrVal(2)) + console.assert(c.limiter.nbQueued() === 1) + console.assert(c.limiter.nbQueued(1) === 0) + console.assert(c.limiter.nbQueued(5) === 1) - c.limiter.submit(c.job, null, 3, c.noErrVal(3)) - console.assert(c.limiter.nbQueued() === 2) - console.assert(c.limiter.nbQueued(1) === 0) - console.assert(c.limiter.nbQueued(5) === 2) + c.limiter.submit(c.job, null, 3, c.noErrVal(3)) + console.assert(c.limiter.nbQueued() === 2) + console.assert(c.limiter.nbQueued(1) === 0) + console.assert(c.limiter.nbQueued(5) === 2) - c.limiter.submit(c.job, null, 4, c.noErrVal(4)) - console.assert(c.limiter.nbQueued() === 3) - console.assert(c.limiter.nbQueued(1) === 0) - console.assert(c.limiter.nbQueued(5) === 3) + c.limiter.submit(c.job, null, 4, c.noErrVal(4)) + console.assert(c.limiter.nbQueued() === 3) + console.assert(c.limiter.nbQueued(1) === 0) + console.assert(c.limiter.nbQueued(5) === 3) - c.limiter.submitPriority(1, c.job, null, 5, c.noErrVal(5)) - console.assert(c.limiter.nbQueued() === 4) - console.assert(c.limiter.nbQueued(1) === 1) - console.assert(c.limiter.nbQueued(5) === 3) + c.limiter.submitPriority(1, c.job, null, 5, c.noErrVal(5)) + console.assert(c.limiter.nbQueued() === 4) + console.assert(c.limiter.nbQueued(1) === 1) + console.assert(c.limiter.nbQueued(5) === 3) - c.last(function (err, results) { - console.assert(c.limiter.nbQueued() === 0) - c.checkResultsOrder([1,5,2,3,4]) - c.checkDuration(1000) - console.assert(c.asserts() === 10) - done() - }) + c.last(function (err, results) { + console.assert(c.limiter.nbQueued() === 0) + c.checkResultsOrder([1,5,2,3,4]) + c.checkDuration(1000) + console.assert(c.asserts() === 10) + done() }) }) + it('Should return the nbRunning', function (done) { + var c = makeTest(2, 250) + + console.assert(c.limiter.nbRunning() === 0) + + c.limiter.submit(c.job, null, 1, c.noErrVal(1)) + console.assert(c.limiter.nbRunning() === 1) + + setTimeout(function () { + console.assert(c.limiter.nbRunning() === 0) + setTimeout(function () { + c.limiter.submit(c.job, null, 1, c.noErrVal(1)) + c.limiter.submit(c.job, null, 2, c.noErrVal(2)) + c.limiter.submit(c.job, null, 3, c.noErrVal(3)) + c.limiter.submit(c.job, null, 4, c.noErrVal(4)) + console.assert(c.limiter.nbRunning() === 2) + done() + }, 0) + }, 0) + }) + describe('Events', function () { - it('Should fire callback on empty queue', function (done) { + it('Should fire events on empty queue', function (done) { var c = makeTest(1, 250) - var called = 0 + var calledEmpty = 0 + var calledIdle = 0 - c.limiter.on('empty', function () { called++ }) + c.limiter.on('empty', function () { calledEmpty++ }) + c.limiter.on('idle', function () { calledIdle++ }) c.pNoErrVal(c.limiter.schedule(c.promise, null, 1), 1) c.pNoErrVal(c.limiter.schedule(c.promise, null, 2), 2) c.pNoErrVal(c.limiter.schedule(c.promise, null, 3), 3) - c.last(function (err, results) { - c.checkResultsOrder([1,2,3]) - c.checkDuration(500) - console.assert(c.asserts() === 3) - console.assert(called === 2) - done() + c.limiter.on('idle', function () { + c.limiter.removeAllListeners() + c.last(function (err, results) { + c.checkResultsOrder([1,2,3]) + c.checkDuration(500) + console.assert(calledEmpty === 2) + console.assert(calledIdle === 1) + done() + }) }) }) - it('Should fire events when calling stopAll()', function (done) { + it('Should fire events when calling stopAll() (sync)', function (done) { var c = makeTest(1, 250) var calledEmpty = 0 + var calledIdle = 0 var calledDropped = 0 c.limiter.on('empty', function () { calledEmpty++ }) + c.limiter.on('idle', function () { calledIdle++ }) c.limiter.on('dropped', function () { calledDropped++ }) c.pNoErrVal(c.limiter.schedule(c.promise, null, 1), 1) @@ -74,8 +100,34 @@ describe('General', function () { setTimeout(function () { console.assert(calledEmpty === 2) console.assert(calledDropped === 2) + console.assert(calledIdle === 0) + done() + }, 30) + }) + + it('Should fire events when calling stopAll() (async)', function (done) { + var c = makeTest(1, 250) + var calledEmpty = 0 + var calledIdle = 0 + var calledDropped = 0 + + c.limiter.on('empty', function () { calledEmpty++ }) + c.limiter.on('idle', function () { calledIdle++ }) + c.limiter.on('dropped', function () { calledDropped++ }) + + c.pNoErrVal(c.limiter.schedule(c.promise, null, 1), 1) + c.pNoErrVal(c.limiter.schedule(c.promise, null, 2), 2) + c.pNoErrVal(c.limiter.schedule(c.promise, null, 3), 3) + + setTimeout(function () { + c.limiter.stopAll() + }, 0) + setTimeout(function () { + console.assert(calledEmpty === 2) + console.assert(calledDropped === 2) + console.assert(calledIdle === 1) done() - }, 20) + }, 30) }) it('Should fail when rejectOnDrop is true', function (done) {