+12
-5
@@ -23,7 +23,14 @@ declare class Sema<T = string> { | ||
| declare function createRateLimiter(rptu: number, { timeUnit, uniformDistribution, }?: { | ||
| timeUnit?: number; | ||
| uniformDistribution?: boolean; | ||
| }): () => Promise<void>; | ||
| declare class RateLimit { | ||
| private sema; | ||
| private delay; | ||
| private timeouts; | ||
| constructor(rate: number, { interval, uniformDistribution, }?: { | ||
| interval?: number; | ||
| uniformDistribution?: boolean; | ||
| }); | ||
| apply(): Promise<NodeJS.Timeout>; | ||
| reset(): number; | ||
| } | ||
| export { Sema, createRateLimiter }; | ||
| export { RateLimit, Sema }; |
+12
-5
@@ -23,7 +23,14 @@ declare class Sema<T = string> { | ||
| declare function createRateLimiter(rptu: number, { timeUnit, uniformDistribution, }?: { | ||
| timeUnit?: number; | ||
| uniformDistribution?: boolean; | ||
| }): () => Promise<void>; | ||
| declare class RateLimit { | ||
| private sema; | ||
| private delay; | ||
| private timeouts; | ||
| constructor(rate: number, { interval, uniformDistribution, }?: { | ||
| interval?: number; | ||
| uniformDistribution?: boolean; | ||
| }); | ||
| apply(): Promise<NodeJS.Timeout>; | ||
| reset(): number; | ||
| } | ||
| export { Sema, createRateLimiter }; | ||
| export { RateLimit, Sema }; |
+42
-28
@@ -11,6 +11,6 @@ 'use strict'; | ||
| var RESIZE_MULTIPLER = 1; | ||
| function arrayMove(src, srcIndex, dst, dstIndex, len) { | ||
| for (let j = 0; j < len; ++j) { | ||
| dst[j + dstIndex] = src[j + srcIndex]; | ||
| src[j + srcIndex] = void 0; | ||
| function arrayMove(list, srcIndex, dstIndex, numberOfElements) { | ||
| for (let j = 0; j < numberOfElements; ++j) { | ||
| list[j + dstIndex] = list[j + srcIndex]; | ||
| list[j + srcIndex] = void 0; | ||
| } | ||
@@ -52,5 +52,4 @@ } | ||
| const length = this._length; | ||
| if (length === 0) { | ||
| return void 0; | ||
| } | ||
| if (length === 0) | ||
| return; | ||
| const i = this._front + length - 1 & this._capacity - 1; | ||
@@ -64,5 +63,4 @@ const ret = this.arr[i]; | ||
| const length = this._length; | ||
| if (length === 0) { | ||
| return void 0; | ||
| } | ||
| if (length === 0) | ||
| return; | ||
| const front = this._front; | ||
@@ -90,3 +88,3 @@ const ret = this.arr[front]; | ||
| const moveItemsCount = front + length & oldCapacity - 1; | ||
| arrayMove(this.arr, 0, this.arr, oldCapacity, moveItemsCount); | ||
| arrayMove(this.arr, 0, oldCapacity, moveItemsCount); | ||
| } | ||
@@ -175,21 +173,37 @@ } | ||
| // src/Utils.ts | ||
| function createRateLimiter(rptu, { | ||
| timeUnit = 1e3, | ||
| uniformDistribution = false | ||
| } = {}) { | ||
| if (!Number.isInteger(rptu) || rptu < 0) { | ||
| throw new TypeError( | ||
| "The rate-limit-per-time-unit (rptu) should be an integer and greater than zero" | ||
| ); | ||
| // src/RateLimit.ts | ||
| var RateLimit = class { | ||
| sema; | ||
| delay; | ||
| timeouts = []; | ||
| constructor(rate, { | ||
| interval = 1e3, | ||
| uniformDistribution = false | ||
| } = {}) { | ||
| if (!Number.isInteger(rate) || rate < 0) { | ||
| throw new TypeError( | ||
| "The rate-limit-per-time-unit (rptu) should be an integer and greater than zero" | ||
| ); | ||
| } | ||
| this.sema = new Sema(uniformDistribution ? 1 : rate); | ||
| this.delay = uniformDistribution ? interval / rate : interval; | ||
| } | ||
| const sema = new Sema(uniformDistribution ? 1 : rptu); | ||
| const delay = uniformDistribution ? timeUnit / rptu : timeUnit; | ||
| return async function limiter() { | ||
| await sema.acquire(); | ||
| setTimeout(() => sema.release(), delay); | ||
| }; | ||
| } | ||
| async apply() { | ||
| await this.sema.acquire(); | ||
| const tm = setTimeout(() => this.sema.release(), this.delay); | ||
| this.timeouts.push(tm); | ||
| return tm; | ||
| } | ||
| reset() { | ||
| const timeoutsToClear = this.timeouts.length; | ||
| this.timeouts.forEach((t) => { | ||
| clearTimeout(t); | ||
| this.sema.release(); | ||
| }); | ||
| this.timeouts = []; | ||
| return timeoutsToClear; | ||
| } | ||
| }; | ||
| exports.RateLimit = RateLimit; | ||
| exports.Sema = Sema; | ||
| exports.createRateLimiter = createRateLimiter; |
+42
-28
@@ -9,6 +9,6 @@ import { EventEmitter } from 'events'; | ||
| var RESIZE_MULTIPLER = 1; | ||
| function arrayMove(src, srcIndex, dst, dstIndex, len) { | ||
| for (let j = 0; j < len; ++j) { | ||
| dst[j + dstIndex] = src[j + srcIndex]; | ||
| src[j + srcIndex] = void 0; | ||
| function arrayMove(list, srcIndex, dstIndex, numberOfElements) { | ||
| for (let j = 0; j < numberOfElements; ++j) { | ||
| list[j + dstIndex] = list[j + srcIndex]; | ||
| list[j + srcIndex] = void 0; | ||
| } | ||
@@ -50,5 +50,4 @@ } | ||
| const length = this._length; | ||
| if (length === 0) { | ||
| return void 0; | ||
| } | ||
| if (length === 0) | ||
| return; | ||
| const i = this._front + length - 1 & this._capacity - 1; | ||
@@ -62,5 +61,4 @@ const ret = this.arr[i]; | ||
| const length = this._length; | ||
| if (length === 0) { | ||
| return void 0; | ||
| } | ||
| if (length === 0) | ||
| return; | ||
| const front = this._front; | ||
@@ -88,3 +86,3 @@ const ret = this.arr[front]; | ||
| const moveItemsCount = front + length & oldCapacity - 1; | ||
| arrayMove(this.arr, 0, this.arr, oldCapacity, moveItemsCount); | ||
| arrayMove(this.arr, 0, oldCapacity, moveItemsCount); | ||
| } | ||
@@ -173,20 +171,36 @@ } | ||
| // src/Utils.ts | ||
| function createRateLimiter(rptu, { | ||
| timeUnit = 1e3, | ||
| uniformDistribution = false | ||
| } = {}) { | ||
| if (!Number.isInteger(rptu) || rptu < 0) { | ||
| throw new TypeError( | ||
| "The rate-limit-per-time-unit (rptu) should be an integer and greater than zero" | ||
| ); | ||
| // src/RateLimit.ts | ||
| var RateLimit = class { | ||
| sema; | ||
| delay; | ||
| timeouts = []; | ||
| constructor(rate, { | ||
| interval = 1e3, | ||
| uniformDistribution = false | ||
| } = {}) { | ||
| if (!Number.isInteger(rate) || rate < 0) { | ||
| throw new TypeError( | ||
| "The rate-limit-per-time-unit (rptu) should be an integer and greater than zero" | ||
| ); | ||
| } | ||
| this.sema = new Sema(uniformDistribution ? 1 : rate); | ||
| this.delay = uniformDistribution ? interval / rate : interval; | ||
| } | ||
| const sema = new Sema(uniformDistribution ? 1 : rptu); | ||
| const delay = uniformDistribution ? timeUnit / rptu : timeUnit; | ||
| return async function limiter() { | ||
| await sema.acquire(); | ||
| setTimeout(() => sema.release(), delay); | ||
| }; | ||
| } | ||
| async apply() { | ||
| await this.sema.acquire(); | ||
| const tm = setTimeout(() => this.sema.release(), this.delay); | ||
| this.timeouts.push(tm); | ||
| return tm; | ||
| } | ||
| reset() { | ||
| const timeoutsToClear = this.timeouts.length; | ||
| this.timeouts.forEach((t) => { | ||
| clearTimeout(t); | ||
| this.sema.release(); | ||
| }); | ||
| this.timeouts = []; | ||
| return timeoutsToClear; | ||
| } | ||
| }; | ||
| export { Sema, createRateLimiter }; | ||
| export { RateLimit, Sema }; |
+1
-1
| { | ||
| "name": "sema4", | ||
| "version": "0.1.0", | ||
| "version": "0.1.1", | ||
| "description": "Promise based Semaphores", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+17
-7
@@ -112,12 +112,22 @@ # sema4 | ||
| #### `createRateLimiter(rptu, { timeUnit, uniformDistribution })` | ||
| ### Rate Limit | ||
| Creates a rate limiter function that blocks with a promise whenever the rate limit is hit and resolves the promise once the call rate is within the limit. | ||
| #### Constructor(rate, { interval, uniformDistribution }) | ||
| Creates a rate limit instance. | ||
| | Name | Type | Optional | Default | Description | | ||
| | ----------------------------- | ------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `rptu` | Integer | No | | Number of tasks allowed per `timeUnit` | | ||
| | `options.timeUnit` | Integer | Yes | 1000 | Defines the width of the rate limiting window in milliseconds | | ||
| | `options.uniformDistribution` | Boolean | Yes | False | Enforces a discrete uniform distribution over time. Setting the `uniformDistribution` option is mainly useful in a situation where the flow of rate limit function calls is continuous and and occurring faster than `timeUnit` (e.g. reading a file) and not enabling it would cause the maximum number of calls to resolve immediately (thus exhaust the limit immediately) and therefore the next bunch of calls would need to wait for `timeUnit` milliseconds. However if the flow is sparse then this option may make the code run slower with no advantages. | | ||
| | `rate` | Integer | No | | Number of tasks allowed per `interval` | | ||
| | `options.interval` | Integer | Yes | 1000 | Defines the width of the rate limiting window in milliseconds | | ||
| | `options.uniformDistribution` | Boolean | Yes | False | Enforces a discrete uniform distribution over time. Setting the `uniformDistribution` option is mainly useful in a situation where the flow of rate limit function calls is continuous and and occurring faster than `interval` (e.g. reading a file) and not enabling it would cause the maximum number of calls to resolve immediately (thus exhaust the limit immediately) and therefore the next bunch of calls would need to wait for `interval` milliseconds. However if the flow is sparse then this option may make the code run slower with no advantages. | | ||
| #### `async rateLimit.apply()` | ||
| Acquires a semaphore and connects a timeout for its release. If the rate limit is reached, the execution process is halted until an available semaphore is released. | ||
| #### `rateLimit.reset()` | ||
| Releases all acquired semaphores immediately and resets the timeouts connected to them. | ||
| ## Examples | ||
@@ -155,6 +165,6 @@ | ||
| async function bar() { | ||
| const lim = RateLimit(5); // Limit to 5 tasks per default timeUnit | ||
| const rl = new RateLimit(5); // Limit to 5 tasks per default time interval | ||
| for (let i = 0; i < n; i++) { | ||
| await lim(); | ||
| await rl.apply(); | ||
| // Perform some async tasks here... | ||
@@ -161,0 +171,0 @@ } |
25589
4.24%427
8.93%190
5.56%