-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRatelimitBucket.ts
70 lines (56 loc) · 1.33 KB
/
RatelimitBucket.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
export class RatelimitBucket {
private observers: (() => void)[] = []
private tokens = this.capacity
private timer?: NodeJS.Timer
constructor(
private capacity: number,
private refill_seconds: number
) {
if (capacity <= 0) {
throw new RangeError(`Invalid capacity: ${capacity}`)
}
if (refill_seconds <= 0) {
throw new RangeError(`Invalid refill seconds: ${refill_seconds}`)
}
}
public starTimer() {
if (this.timer != undefined) {
throw Error(`Timer already started!`)
}
const fillDelayMs = 1000 * this.refill_seconds / this.capacity
this.timer = setInterval(() => this.addToken(), fillDelayMs)
}
public stopTimer() {
if (this.timer == undefined) {
throw Error(`No timer present!`)
}
clearInterval(this.timer)
}
private addToken() {
this.tokens = Math.min(this.tokens + 1, this.capacity)
if (this.tokens > 0) {
const observer = this.observers.splice(0, 1)
if (observer.length != 0 && this.allocate()) {
observer[0]()
}
}
}
/* Consume a ratelimit token, if present */
public allocate(): boolean {
if (this.tokens > 0) {
this.tokens--
return true
}
return false
}
/* Wait until a token can be consumed */
public wait(): Promise<void> {
return new Promise((resolve) => {
if (this.allocate()) {
resolve()
return
}
this.observers.push(resolve)
})
}
}