@cygnus-wealth/asset-valuator
Version:
Asset valuation library for retrieving and converting cryptocurrency prices
61 lines (60 loc) • 2.22 kB
JavaScript
export class RateLimiter {
constructor(options) {
this.options = options;
this.requests = [];
this.retryCount = new Map();
this.options.retryAfterMs = options.retryAfterMs || 1000;
this.options.maxRetries = options.maxRetries || 3;
}
async execute(key, fn) {
const now = Date.now();
// Clean up old requests
this.requests = this.requests.filter(time => now - time < this.options.windowMs);
// Check if we're at the limit
if (this.requests.length >= this.options.maxRequests) {
const retries = this.retryCount.get(key) || 0;
if (retries >= this.options.maxRetries) {
this.retryCount.delete(key);
throw new Error('Rate limit exceeded. Please try again later.');
}
// Exponential backoff
const backoffMs = this.options.retryAfterMs * Math.pow(2, retries);
await this.sleep(backoffMs);
this.retryCount.set(key, retries + 1);
return this.execute(key, fn);
}
// Execute the function
this.requests.push(now);
this.retryCount.delete(key);
try {
return await fn();
}
catch (error) {
// If it's a 429 error, apply backoff
if (this.is429Error(error)) {
const retries = this.retryCount.get(key) || 0;
if (retries >= this.options.maxRetries) {
this.retryCount.delete(key);
throw error;
}
const backoffMs = this.options.retryAfterMs * Math.pow(2, retries);
await this.sleep(backoffMs);
this.retryCount.set(key, retries + 1);
return this.execute(key, fn);
}
throw error;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
is429Error(error) {
return error?.response?.status === 429 ||
error?.code === 'ERR_RATE_LIMITED' ||
(error?.message && error.message.includes('429'));
}
reset() {
this.requests = [];
this.retryCount.clear();
}
}