UNPKG

5.88 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 * msBetweenTwoCalls: {integer}, // default is 0 milliseconds (no waiting time between two calls)
13 * minutelyLimit: {integer}, // default is Infinity (no minutely limit set)
14 * hourlyLimit: {integer}, // default is Infinity (no hourly limit set)
15 * test: {boolean}, // default is false (if true, max waiting time is 5 secs)
16 * }
17 *
18 * var limitWaiter = waiter.LimitWaiter(options);
19 *
20 * let myApiCallFunction = async () => {
21 * limitWaiter.wait(function(){
22 * // your api call
23 * })
24 * }
25 *
26 * @param {Object} options
27 * @constructor
28 */
29function LimitWaiter(options) {
30 this.msWait = options ? options.msBetweenTwoCalls ? Number.isSafeInteger(options.msBetweenTwoCalls) ? options.msBetweenTwoCalls > 0 ? options.msBetweenTwoCalls : 0 : 0 : 0 : 0;
31 this.mLim = options ? options.minutelyLimit ? Number.isSafeInteger(options.minutelyLimit) ? options.minutelyLimit > 0 ? options.minutelyLimit : x : x : x : x;
32 this.hLim = options ? options.hourlyLimit ? Number.isSafeInteger(options.hourlyLimit) ? options.hourlyLimit > 0 ? options.hourlyLimit : x : x : x : x;
33 this.startWaitingCallback = options ? options.startWaitingCallback ? options.startWaitingCallback : function () { } : function () { };
34 this.endWaitingCallback = options ? options.endWaitingCallback ? options.endWaitingCallback : function () { } : function () { };
35 this.waitingTickCallback = options ? options.waitingTickCallback ? options.waitingTickCallback : function () { } : function () { };
36 this.test = options ? options.test ? options.test : false : false;
37 this.mC = 0;
38 this.hC = 0;
39 this.totalC = 0;
40 this.callbackQueue = [];
41 this.alreadyWorking = false;
42}
43
44LimitWaiter.prototype.wait = function (apiCallFunction) {
45 this.callbackQueue.push(apiCallFunction);
46 if (!this.alreadyWorking) {
47 workOnQueueLW(this); // trigger working
48 }
49}
50
51let workOnQueueLW = async (ctx) => {
52 while (ctx.callbackQueue.length > 0) {
53 ctx.alreadyWorking = true;
54 await checkWaitingLW(ctx)
55 .then(() => {
56 let cb = ctx.callbackQueue[0];
57 ctx.callbackQueue.shift();
58 cb();
59 // manage counters
60 ctx.mC++;
61 ctx.hC++;
62 ctx.totalC++;
63 })
64 }
65 ctx.alreadyWorking = false;
66}
67
68function checkWaitingLW(ctx) {
69 return new Promise(function (resolve) {
70 if (ctx.mC < ctx.mLim && ctx.hC < ctx.hLim) {
71 if (ctx.msWait > 0) {
72 ctx.startWaitingCallback({
73 millisecondsToWait: ctx.msWait
74 })
75 waitMilliseconds(ctx.msWait)
76 .then(() => {
77 ctx.endWaitingCallback({
78 millisecondsWaited: ctx.msWait
79 })
80 resolve();
81 })
82 } else {
83 resolve(); // do not have to wait
84 }
85 } else if (ctx.hC >= ctx.hLim) {
86 let d = new Date().getMinutes();
87 let minutes = 60 - d;
88 let seconds = minutes * 60;
89 ctx.startWaitingCallback({
90 currentCallsInAnHour: ctx.hC,
91 hourlyLimit: ctx.hLim,
92 secondsToWaitTilNextHour: seconds
93 })
94 seconds = ctx.test ? 5 : seconds;
95 waitSeconds(seconds, ctx.waitingTickCallback)
96 .then(() => {
97 ctx.hC = ctx.mC = 0;
98 ctx.endWaitingCallback({
99 secondsWaited: seconds,
100 callsInQueue: ctx.callbackQueue.length,
101 totalCalls: ctx.totalC
102 })
103 resolve()
104 })
105 } else if (ctx.mC >= ctx.mLim) {
106 let d = new Date().getSeconds();
107 let seconds = 60 - d;
108 ctx.startWaitingCallback({
109 currentCallsInAMinute: ctx.mC,
110 minutelyLimit: ctx.mLim,
111 secondsToWaitTilNextMinute: seconds
112 })
113 seconds = ctx.test ? 5 : seconds;
114 waitSeconds(seconds, ctx.waitingTickCallback)
115 .then(() => {
116 ctx.mC = 0;
117 ctx.endWaitingCallback({
118 secondsWaited: seconds,
119 callsInQueue: ctx.callbackQueue.length,
120 totalCalls: ctx.totalC
121 })
122 resolve()
123 })
124 }
125 })
126}
127
128function waitMilliseconds(milliseconds) {
129 return new Promise(function (resolve) {
130 let t = setTimeout(function () {
131 clearTimeout(t);
132 resolve();
133 }, milliseconds)
134 })
135}
136
137function waitSeconds(seconds, tickCallback) {
138 return new Promise(function (resolve) {
139 let count = 1;
140 let v = setInterval(function () {
141 tickCallback({
142 secondsToWait: seconds - count,
143 secondsWaited: count
144 })
145 count++;
146 if (count > seconds) {
147 clearInterval(v);
148 resolve();
149 }
150 }, 1000)
151 })
152}
153
154exports.LimitWaiter = LimitWaiter;
\No newline at end of file