Last active
February 24, 2023 13:39
-
-
Save lihaibh/f3abbf999ef3433199342c2d4ab0ff9e to your computer and use it in GitHub Desktop.
Rate limit wrapper - wraps an async function, make sure the calls do not exceed the rate limit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function withRateLimit<T, E extends (...args: any[]) => Promise<T>>({ fn, limit, every }: { | |
fn: E, | |
// maximum amount of executions in specific amount of time | |
limit: number, | |
// the amount in milliseconds we are limiting the total amount of executions | |
every: number | |
}): E { | |
let lastExecTime = -1; | |
let requestCounter = 0; | |
let triggerFlushTimeout = null; | |
let todo = []; | |
/** | |
* Flush the requests in the todo queue based on the rate limit. | |
*/ | |
async function flush() { | |
// Check if it's been more than a minute since the last execution | |
if (Date.now() - lastExecTime > every) { | |
requestCounter = 0; | |
} | |
// flush more requests from the todo queue | |
if (requestCounter < limit) { | |
const nextCalls = todo.splice(0, limit - requestCounter); | |
if (nextCalls.length > 0) { | |
requestCounter += nextCalls.length; | |
lastExecTime = Date.now(); | |
// execute the requests without waiting them | |
for (const nextCall of nextCalls) { | |
nextCall(); | |
} | |
} | |
} | |
// if there are still requests in the todo queue, wait for the remaining time and re-flush | |
if (todo.length > 0 && !triggerFlushTimeout) { | |
const remainingTime = (every - (Date.now() - lastExecTime)) + 10; | |
// trigger flush again | |
triggerFlushTimeout = setTimeout(() => { | |
clearTimeout(triggerFlushTimeout); | |
triggerFlushTimeout = null; | |
flush(); | |
}, remainingTime); | |
} | |
} | |
return (async (...args: any[]) => { | |
// await until the function executes by the flush mechanism | |
const promise = new Promise<T>((resolve, reject) => { | |
// this the todo requests, will be executed lazily later | |
todo.push(async () => { | |
return fn(...args).then(resolve).catch(reject); | |
}); | |
}); | |
flush(); | |
return promise; | |
}) as E; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment