throttled-queue
Advanced tools
Comparing version 2.0.3 to 2.1.0
@@ -13,4 +13,5 @@ "use strict"; | ||
var queue = []; | ||
var lastCalled = 0; | ||
var timeout = undefined; | ||
var lastIntervalStart = 0; | ||
var numRequestsPerInterval = 0; | ||
var timeout; | ||
/** | ||
@@ -21,3 +22,3 @@ * Gets called at a set interval to remove items from the queue. | ||
var dequeue = function () { | ||
var threshold = lastCalled + interval; | ||
var intervalEnd = lastIntervalStart + interval; | ||
var now = Date.now(); | ||
@@ -27,13 +28,15 @@ /** | ||
*/ | ||
if (now < threshold) { | ||
if (now < intervalEnd) { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
timeout && clearTimeout(timeout); | ||
timeout = setTimeout(dequeue, threshold - now); | ||
timeout !== undefined && clearTimeout(timeout); | ||
timeout = setTimeout(dequeue, intervalEnd - now); | ||
return; | ||
} | ||
lastIntervalStart = now; | ||
numRequestsPerInterval = 0; | ||
for (var _i = 0, _a = queue.splice(0, maxRequestsPerInterval); _i < _a.length; _i++) { | ||
var callback = _a[_i]; | ||
callback.call({}); | ||
numRequestsPerInterval++; | ||
void callback(); | ||
} | ||
lastCalled = Date.now(); | ||
if (queue.length) { | ||
@@ -47,6 +50,17 @@ timeout = setTimeout(dequeue, interval); | ||
return function (fn) { return new Promise(function (resolve, reject) { | ||
queue.push(function () { return Promise.resolve().then(fn).then(resolve).catch(reject); }); | ||
if (!timeout) { | ||
timeout = setTimeout(dequeue, interval); | ||
var callback = function () { return Promise.resolve().then(fn).then(resolve).catch(reject); }; | ||
var now = Date.now(); | ||
if (timeout === undefined && (now - lastIntervalStart) > interval) { | ||
lastIntervalStart = now; | ||
numRequestsPerInterval = 0; | ||
} | ||
if (numRequestsPerInterval++ < maxRequestsPerInterval) { | ||
void callback(); | ||
} | ||
else { | ||
queue.push(callback); | ||
if (timeout === undefined) { | ||
timeout = setTimeout(dequeue, lastIntervalStart + interval - now); | ||
} | ||
} | ||
}); }; | ||
@@ -53,0 +67,0 @@ } |
{ | ||
"name": "throttled-queue", | ||
"version": "2.0.3", | ||
"version": "2.1.0", | ||
"description": "Throttles arbitrary code to execute a maximum number of times per interval. Best for making throttled API requests.", | ||
@@ -5,0 +5,0 @@ "main": "dist/throttledQueue.js", |
@@ -114,7 +114,7 @@ # throttled-queue | ||
const usernames = ['shaunpersad', 'forward-motion']; | ||
const profiles = await Promise.all(usernames.map((username) => { | ||
return throttle(() => { | ||
const profiles = await Promise.all( | ||
usernames.map((username) => throttle(() => { | ||
return fetch(`https://api.github.com/search/users?q=${username}`); | ||
}); | ||
})); | ||
})) | ||
); | ||
@@ -128,3 +128,3 @@ const justMe = await throttle(() => fetch('https://api.github.com/search/users?q=shaunpersad')); | ||
However, you may also specify the return type when needed: | ||
However, you may also specify the return type of the promise when needed: | ||
```typescript | ||
@@ -131,0 +131,0 @@ import throttledQueue from 'throttled-queue'; |
@@ -14,4 +14,5 @@ function throttledQueue( | ||
const queue: Array<() => Promise<void>> = []; | ||
let lastCalled = 0; | ||
let timeout: NodeJS.Timeout | undefined = undefined; | ||
let lastIntervalStart = 0; | ||
let numRequestsPerInterval = 0; | ||
let timeout: NodeJS.Timeout | undefined; | ||
/** | ||
@@ -22,3 +23,3 @@ * Gets called at a set interval to remove items from the queue. | ||
const dequeue = () => { | ||
const threshold = lastCalled + interval; | ||
const intervalEnd = lastIntervalStart + interval; | ||
const now = Date.now(); | ||
@@ -28,12 +29,14 @@ /** | ||
*/ | ||
if (now < threshold) { | ||
if (now < intervalEnd) { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
timeout && clearTimeout(timeout); | ||
timeout = setTimeout(dequeue, threshold - now); | ||
timeout !== undefined && clearTimeout(timeout); | ||
timeout = setTimeout(dequeue, intervalEnd - now); | ||
return; | ||
} | ||
lastIntervalStart = now; | ||
numRequestsPerInterval = 0; | ||
for (const callback of queue.splice(0, maxRequestsPerInterval)) { | ||
callback.call({}); | ||
numRequestsPerInterval++; | ||
void callback(); | ||
} | ||
lastCalled = Date.now(); | ||
if (queue.length) { | ||
@@ -48,6 +51,16 @@ timeout = setTimeout(dequeue, interval); | ||
(resolve, reject) => { | ||
queue.push(() => Promise.resolve().then(fn).then(resolve).catch(reject)); | ||
if (!timeout) { | ||
timeout = setTimeout(dequeue, interval); | ||
const callback = () => Promise.resolve().then(fn).then(resolve).catch(reject); | ||
const now = Date.now(); | ||
if (timeout === undefined && (now - lastIntervalStart) > interval) { | ||
lastIntervalStart = now; | ||
numRequestsPerInterval = 0; | ||
} | ||
if (numRequestsPerInterval++ < maxRequestsPerInterval) { | ||
void callback(); | ||
} else { | ||
queue.push(callback); | ||
if (timeout === undefined) { | ||
timeout = setTimeout(dequeue, lastIntervalStart + interval - now); | ||
} | ||
} | ||
}, | ||
@@ -54,0 +67,0 @@ ); |
import throttledQueue from '../src/throttledQueue'; | ||
function calculateRPMS(numRequests: number, timeStarted: number) { | ||
return numRequests / (Date.now() - timeStarted); | ||
} | ||
describe('throttled-queue', function () { | ||
it('should queue all fns', function (done) { | ||
it('should queue all fns', function () { | ||
const requestsPerInterval = 1; | ||
@@ -20,101 +13,71 @@ const interval = 200; | ||
void throttle(() => { | ||
console.log('Throttling...'); | ||
numRequests++; | ||
}); | ||
} | ||
void throttle(() => { | ||
return throttle(() => { | ||
if (numRequests !== requestLimit) { | ||
throw new Error('Not all callbacks queued.'); | ||
} | ||
done(); | ||
}); | ||
}); | ||
it('should queue the fn within the interval', function (done) { | ||
it('should queue the fn and honor the interval', function () { | ||
const requestsPerInterval = 1; | ||
const interval = 200; | ||
const interval = 500; | ||
const throttle = throttledQueue(requestsPerInterval, interval); | ||
let lastExecuted = Date.now(); | ||
const requestLimit = 100; | ||
let lastIntervalStart = process.hrtime.bigint(); | ||
let numRequests = 0; | ||
const requestLimit = 100; | ||
let numRequestsPerInterval = 0; | ||
for (let x = 0; x < requestLimit; x++) { | ||
void throttle(() => { | ||
console.log('Throttling...'); | ||
const now = Date.now(); | ||
const timeElapsed = now - lastExecuted; | ||
if (timeElapsed < interval) { | ||
if ((process.hrtime.bigint() - lastIntervalStart) > (interval * 1000000)) { | ||
lastIntervalStart = process.hrtime.bigint(); | ||
numRequestsPerInterval = 0; | ||
} | ||
if (++numRequestsPerInterval > requestsPerInterval) { | ||
throw new Error('Did not honor interval.'); | ||
} | ||
lastExecuted = now; | ||
numRequests++; | ||
}); | ||
} | ||
void throttle(() => { | ||
return throttle(() => { | ||
if (numRequests !== requestLimit) { | ||
throw new Error('Not all callbacks queued.'); | ||
} | ||
done(); | ||
}); | ||
}); | ||
it('should queue the fn and honor the interval', function (done) { | ||
it('should queue the fn and honor the interval with multiple requests per interval', function () { | ||
const requestsPerInterval = 1; | ||
const interval = 500; | ||
const throttle = throttledQueue(requestsPerInterval, interval); | ||
const timeStarted = Date.now(); | ||
const maxRpms = requestsPerInterval / interval; | ||
let numRequests = 0; | ||
const requestLimit = 100; | ||
for (let x = 0; x < requestLimit; x++) { | ||
void throttle(() => { | ||
const rpms = calculateRPMS(++numRequests, timeStarted); | ||
console.log(rpms, maxRpms); | ||
if (rpms > maxRpms) { | ||
throw new Error('Did not honor interval.'); | ||
} | ||
}); | ||
} | ||
void throttle(() => { | ||
if (numRequests !== requestLimit) { | ||
throw new Error('Not all callbacks queued.'); | ||
} | ||
done(); | ||
}); | ||
}); | ||
it('should queue the fn and honor the interval with multiple requests per interval', function (done) { | ||
const requestsPerInterval = 3; | ||
const interval = 1000; | ||
const throttle = throttledQueue(requestsPerInterval, interval); | ||
const timeStarted = Date.now(); | ||
const maxRpms = requestsPerInterval / interval; | ||
const requestLimit = 100; | ||
let lastIntervalStart = process.hrtime.bigint(); | ||
let numRequests = 0; | ||
const requestLimit = 100; | ||
let numRequestsPerInterval = 0; | ||
for (let x = 0; x < requestLimit; x++) { | ||
void throttle(() => { | ||
const rpms = calculateRPMS(++numRequests, timeStarted); | ||
console.log(rpms, maxRpms); | ||
if (rpms > maxRpms) { | ||
if ((process.hrtime.bigint() - lastIntervalStart) > (interval * 1000000)) { | ||
lastIntervalStart = process.hrtime.bigint(); | ||
numRequestsPerInterval = 0; | ||
} | ||
if (++numRequestsPerInterval > requestsPerInterval) { | ||
throw new Error('Did not honor interval.'); | ||
} | ||
numRequests++; | ||
}); | ||
} | ||
void throttle(() => { | ||
return throttle(() => { | ||
if (numRequests !== requestLimit) { | ||
throw new Error('Not all callbacks queued.'); | ||
} | ||
done(); | ||
}); | ||
}); | ||
it('should queue the fn and honor the interval with multiple evenly spaced requests per interval', function (done) { | ||
it('should queue the fn and honor the interval with multiple evenly spaced requests per interval', function () { | ||
@@ -124,22 +87,23 @@ const requestsPerInterval = 3; | ||
const throttle = throttledQueue(requestsPerInterval, interval, true); | ||
const timeStarted = Date.now(); | ||
const maxRpms = requestsPerInterval / interval; | ||
const requestLimit = 100; | ||
let lastIntervalStart = process.hrtime.bigint(); | ||
let numRequests = 0; | ||
const requestLimit = 100; | ||
let numRequestsPerInterval = 0; | ||
for (let x = 0; x < requestLimit; x++) { | ||
void throttle(() => { | ||
const rpms = calculateRPMS(++numRequests, timeStarted); | ||
console.log(rpms, maxRpms); | ||
if (rpms > maxRpms) { | ||
if ((process.hrtime.bigint() - lastIntervalStart) > (interval * 1000000)) { | ||
lastIntervalStart = process.hrtime.bigint(); | ||
numRequestsPerInterval = 0; | ||
} | ||
if (++numRequestsPerInterval > requestsPerInterval) { | ||
throw new Error('Did not honor interval.'); | ||
} | ||
numRequests++; | ||
}); | ||
} | ||
void throttle(() => { | ||
return throttle(() => { | ||
if (numRequests !== requestLimit) { | ||
throw new Error('Not all callbacks queued.'); | ||
} | ||
done(); | ||
}); | ||
@@ -146,0 +110,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
25325
387