-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathaxiosRateLimit.ts
98 lines (78 loc) · 2.24 KB
/
axiosRateLimit.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
* inspired by axios-rate-limit with a few adjustments and bug fixes
* https://github.com/aishek/axios-rate-limit
*/
import { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
type RateLimitRequestHandler = {
resolve: () => boolean;
};
type RateLimitOptions =
| {
maxRPS: number;
}
| {
maxRequests: number;
perMilliseconds: number;
};
function throwIfCancellationRequested(config: InternalAxiosRequestConfig<any>) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
export class AxiosRateLimit {
private queue: RateLimitRequestHandler[];
private timeslotRequests: number;
private perMilliseconds: number;
private maxRequests: number;
constructor(axios: AxiosInstance, options: RateLimitOptions) {
this.queue = [];
this.timeslotRequests = 0;
if ('maxRPS' in options) {
this.perMilliseconds = 1000;
this.maxRequests = options.maxRPS;
} else {
this.perMilliseconds = options.perMilliseconds;
this.maxRequests = options.maxRequests;
}
function handleError(error: unknown) {
return Promise.reject(error);
}
axios.interceptors.request.use(this.handleRequest, handleError);
}
private handleRequest = (request: InternalAxiosRequestConfig<any>) => {
return new Promise<InternalAxiosRequestConfig<any>>((resolve, reject) => {
this.push({
resolve: function () {
try {
throwIfCancellationRequested(request);
} catch (error) {
reject(error);
return false;
}
resolve(request);
return true;
},
});
});
};
private push = (requestHandler: RateLimitRequestHandler) => {
this.queue.push(requestHandler);
this.shift();
};
private onRequestTimerMet = () => {
this.timeslotRequests--;
this.shift();
};
private shift = () => {
if (this.timeslotRequests >= this.maxRequests) return;
const queued = this.queue.shift();
if (!queued) return;
const resolved = queued.resolve();
if (!resolved) {
this.shift(); // rejected request --> shift another request
return;
}
this.timeslotRequests++;
setTimeout(this.onRequestTimerMet, this.perMilliseconds);
};
}