Last active
January 29, 2019 10:23
-
-
Save davidpurkiss/191c4dbb714154e42b5ca4f8a8fc0cf1 to your computer and use it in GitHub Desktop.
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
import moment = require('moment'); | |
export class RequestThrottler { | |
requestTimes: moment.Moment[]; | |
requestRateLimit: number; | |
requestRateWindow: moment.Duration; | |
pendingRequests: { | |
resolve: (value?: any) => void; | |
reject: (reason?: any) => void; | |
request: () => Promise<any>; | |
}[]; | |
timer: NodeJS.Timer; | |
constructor(requestRateLimit: number, requestRateWindowInMilliseconds: number) { | |
this.requestRateLimit = requestRateLimit; | |
this.requestRateWindow = moment.duration(requestRateWindowInMilliseconds); | |
this.requestTimes = []; | |
this.pendingRequests = []; | |
} | |
async execute(request: () => Promise<any>): Promise<any> { | |
if (this.currentRequestRate < this.requestRateLimit) { | |
this.requestTimes.push(moment()); | |
return await request(); | |
} else { | |
this.scheduleCheckPendingRequests(); | |
return new Promise<any>(async (resolve, reject) => { | |
this.pendingRequests.push({ | |
resolve: resolve, | |
reject: reject, | |
request: request | |
}); | |
}); | |
} | |
} | |
private async scheduleCheckPendingRequests() { | |
this.timer = setTimeout(async () => { | |
this.checkPendingRequests(); | |
}, 100); | |
} | |
private async checkPendingRequests() { | |
if (this.pendingRequests.length === 0) { | |
this.scheduleCheckPendingRequests(); | |
return; | |
} | |
if (this.currentRequestRate < this.requestRateLimit) { | |
const pendingRequestsToExecute = this.requestRateLimit - this.currentRequestRate; | |
let requestsToExecute = this.pendingRequests.slice(0, pendingRequestsToExecute); | |
this.pendingRequests = this.pendingRequests.slice(pendingRequestsToExecute + 1, this.pendingRequests.length); | |
for (let request of requestsToExecute) { | |
try { | |
this.requestTimes.push(moment()); | |
request.resolve(await request.request()); | |
} catch (error) { | |
request.reject(error); | |
} | |
} | |
this.scheduleCheckPendingRequests(); | |
} else { | |
this.scheduleCheckPendingRequests(); | |
} | |
} | |
get currentRequestRate(): number { | |
return this.requestTimes.filter(t => t.isSameOrAfter(moment().subtract(this.requestRateWindow))).length; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment