-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy paththrottle.ts
118 lines (98 loc) · 3.3 KB
/
throttle.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { lru } from 'tiny-lru';
// @ts-ignore
import { isAbsoluteURL, joinPath, buildSortedURL } from 'xior';
import { ICacheLike } from './cache/utils';
import type { XiorPlugin, XiorRequestConfig, XiorResponse } from '../types';
type XiorPromise = Promise<XiorResponse>;
export type RecordedCache = {
timestamp: number;
value?: XiorPromise;
};
export type XiorThrottleOptions = {
/** threshold in milliseconds, default: 1000ms */
threshold?: number;
/**
* check if we need enable throttle, default only `GET` method or`isGet: true` enable
*/
enableThrottle?: boolean | ((config?: XiorRequestConfig) => boolean);
throttleCache?: ICacheLike<RecordedCache>;
onThrottle?: (config: XiorRequestConfig) => void;
/** max throttle numbers in LRU, default is 100 */
throttleItems?: number;
};
/** @ts-ignore */
declare module 'xior' {
interface XiorRequestConfig extends Omit<XiorThrottleOptions, 'throttleCache' | 'throttleItems'> {
//
}
}
export default function xiorThrottlePlugin(options: XiorThrottleOptions = {}): XiorPlugin {
const {
enableThrottle: _enableThrottle,
threshold: _threshold = 1000,
throttleCache = lru<RecordedCache>(options.throttleItems || 100),
onThrottle: _onThrottle,
} = options;
const cache = throttleCache;
return function (adapter) {
const recordCacheWithRequest = (index: string, config: XiorRequestConfig) => {
const responsePromise = (async () => {
try {
const response = await adapter(config);
cache.set(index, {
timestamp: Date.now(),
value: Promise.resolve(response),
});
return response;
} catch (reason) {
if ('delete' in cache) {
cache.delete(index);
} else {
cache.del(index);
}
throw reason;
}
})();
cache.set(index, {
timestamp: Date.now(),
value: responsePromise,
});
return responsePromise;
};
return async (config) => {
const {
paramsSerializer,
threshold = _threshold,
enableThrottle = _enableThrottle,
onThrottle = _onThrottle,
} = config as XiorThrottleOptions & XiorRequestConfig;
const isGet = config.method === 'GET' || config.isGet;
const t = typeof enableThrottle;
let enabled: boolean | undefined = undefined;
if (t === 'function') {
enabled = (enableThrottle as (config: XiorRequestConfig) => boolean | undefined)(config);
}
if (enabled === undefined) {
enabled = t === 'undefined' ? isGet : Boolean(enableThrottle);
}
if (enabled) {
const index = buildSortedURL(
config.url && isAbsoluteURL(config.url)
? config.url
: joinPath(config.baseURL, config.url),
{ a: config.data, b: config.params },
paramsSerializer as (obj: Record<string, any>) => string
);
const now = Date.now();
const cachedRecord = cache.get(index) || { timestamp: now };
const responsePromise = cachedRecord.value;
if (responsePromise && now - cachedRecord.timestamp <= threshold) {
onThrottle?.(config);
return responsePromise;
}
return recordCacheWithRequest(index, config);
}
return adapter(config);
};
};
}