UNPKG

4.9 kBJavaScriptView Raw
1const x = Infinity;
2
3/**
4 * Waiter based on API rate limits you enter manually
5 *
6 * import patiently from "patiently";
7 *
8 * let options = {
9 * startWaitingCallback: {function}, // default is function(){}, calls a function if waiting necessary
10 * endWaitingCallback: {function}, // default is function(){}, calls a function after waiting
11 * waitingTickCallback: {function}, // default is function(){}, calls a function every tick
12 * minutelyLimit: {integer}, // default is Infinity (no minutely limit set)
13 * hourlyLimit: {integer}, // default is Infinity (no hourly limit set)
14 * test: {boolean}, // default is false (if true, max waiting time is 5 secs)
15 * }
16 *
17 * var limitWaiter = waiter.LimitWaiter(options);
18 *
19 * let myApiCallFunction = async () => {
20 * limitWaiter.wait(function(){
21 * // your api call
22 * })
23 * }
24 *
25 * @param {Object} options
26 * @constructor
27 */
28function LimitWaiter(options) {
29 this.mLim = options ? options.minutelyLimit ? Number.isSafeInteger(options.minutelyLimit) ? options.minutelyLimit > 0 ? options.minutelyLimit : x : x : x : x;
30 this.hLim = options ? options.hourlyLimit ? Number.isSafeInteger(options.hourlyLimit) ? options.hourlyLimit > 0 ? options.hourlyLimit : x : x : x : x;
31 this.startWaitingCallback = options ? options.startWaitingCallback ? options.startWaitingCallback : function () { } : function () { };
32 this.endWaitingCallback = options ? options.endWaitingCallback ? options.endWaitingCallback : function () { } : function () { };
33 this.waitingTickCallback = options ? options.waitingTickCallback ? options.waitingTickCallback : function () { } : function () { };
34 this.test = options ? options.test ? options.test : false : false;
35 this.mC = 0;
36 this.hC = 0;
37 this.totalC = 0;
38 this.callbackQueue = [];
39 this.alreadyWorking = false;
40}
41
42LimitWaiter.prototype.wait = function (apiCallFunction) {
43 this.callbackQueue.push(apiCallFunction);
44 if (!this.alreadyWorking) {
45 workOnQueueLW(this); // trigger working
46 }
47}
48
49let workOnQueueLW = async (ctx) => {
50 while (ctx.callbackQueue.length > 0) {
51 ctx.alreadyWorking = true;
52 await checkWaitingLW(ctx)
53 .then(() => {
54 let cb = ctx.callbackQueue[0];
55 ctx.callbackQueue.shift();
56 cb();
57 // manage counters
58 ctx.mC++;
59 ctx.hC++;
60 ctx.totalC++;
61 })
62 }
63 ctx.alreadyWorking = false;
64}
65
66function checkWaitingLW(ctx) {
67 return new Promise(function (resolve) {
68 if (ctx.mC < ctx.mLim && ctx.hC < ctx.hLim) {
69 resolve(); // do not have to wait
70 } else if (ctx.hC >= ctx.hLim) {
71 let d = new Date().getMinutes();
72 let minutes = 60 - d;
73 let seconds = minutes * 60;
74 ctx.startWaitingCallback({
75 currentCallsInAnHour: ctx.hC,
76 hourlyLimit: ctx.hLim,
77 secondsToWaitTilNextHour: seconds
78 })
79 seconds = ctx.test ? 5 : seconds;
80 waitSeconds(seconds, ctx.waitingTickCallback)
81 .then(() => {
82 ctx.hC = ctx.mC = 0;
83 ctx.endWaitingCallback({
84 secondsWaited: seconds,
85 callsInQueue: ctx.callbackQueue.length,
86 totalCalls: ctx.totalC
87 })
88 resolve()
89 })
90 } else if (ctx.mC >= ctx.mLim) {
91 let d = new Date().getSeconds();
92 let seconds = 60 - d;
93 ctx.startWaitingCallback({
94 currentCallsInAMinute: ctx.mC,
95 minutelyLimit: ctx.mLim,
96 secondsToWaitTilNextMinute: seconds
97 })
98 seconds = ctx.test ? 5 : seconds;
99 waitSeconds(seconds, ctx.waitingTickCallback)
100 .then(() => {
101 ctx.mC = 0;
102 ctx.endWaitingCallback({
103 secondsWaited: seconds,
104 callsInQueue: ctx.callbackQueue.length,
105 totalCalls: ctx.totalC
106 })
107 resolve()
108 })
109 }
110 })
111}
112
113function waitSeconds(seconds, tickCallback) {
114 return new Promise(function (resolve) {
115 let count = 1;
116 let v = setInterval(function () {
117 tickCallback({
118 secondsToWait: seconds - count,
119 secondsWaited: count
120 })
121 count++;
122 if (count > seconds) {
123 clearInterval(v);
124 resolve();
125 }
126 }, 1000)
127 })
128}
129
130exports.LimitWaiter = LimitWaiter;
\No newline at end of file