UNPKG

4.46 kBJavaScriptView Raw
1/**
2 * Array.prototype.findIndex ponyfill for old browsers.
3 */
4function findIndex(array, predicate) {
5 for (var i = 0; i < array.length; i++) {
6 if (predicate(array[i])) return i;
7 }
8
9 return -1;
10}
11
12function createCancelError() {
13 return new Error('Cancelled');
14}
15
16module.exports = /*#__PURE__*/function () {
17 function RateLimitedQueue(limit) {
18 if (typeof limit !== 'number' || limit === 0) {
19 this.limit = Infinity;
20 } else {
21 this.limit = limit;
22 }
23
24 this.activeRequests = 0;
25 this.queuedHandlers = [];
26 }
27
28 var _proto = RateLimitedQueue.prototype;
29
30 _proto._call = function _call(fn) {
31 var _this = this;
32
33 this.activeRequests += 1;
34 var _done = false;
35 var cancelActive;
36
37 try {
38 cancelActive = fn();
39 } catch (err) {
40 this.activeRequests -= 1;
41 throw err;
42 }
43
44 return {
45 abort: function abort() {
46 if (_done) return;
47 _done = true;
48 _this.activeRequests -= 1;
49 cancelActive();
50
51 _this._queueNext();
52 },
53 done: function done() {
54 if (_done) return;
55 _done = true;
56 _this.activeRequests -= 1;
57
58 _this._queueNext();
59 }
60 };
61 };
62
63 _proto._queueNext = function _queueNext() {
64 var _this2 = this;
65
66 // Do it soon but not immediately, this allows clearing out the entire queue synchronously
67 // one by one without continuously _advancing_ it (and starting new tasks before immediately
68 // aborting them)
69 Promise.resolve().then(function () {
70 _this2._next();
71 });
72 };
73
74 _proto._next = function _next() {
75 if (this.activeRequests >= this.limit) {
76 return;
77 }
78
79 if (this.queuedHandlers.length === 0) {
80 return;
81 } // Dispatch the next request, and update the abort/done handlers
82 // so that cancelling it does the Right Thing (and doesn't just try
83 // to dequeue an already-running request).
84
85
86 var next = this.queuedHandlers.shift();
87
88 var handler = this._call(next.fn);
89
90 next.abort = handler.abort;
91 next.done = handler.done;
92 };
93
94 _proto._queue = function _queue(fn, options) {
95 var _this3 = this;
96
97 if (options === void 0) {
98 options = {};
99 }
100
101 var handler = {
102 fn: fn,
103 priority: options.priority || 0,
104 abort: function abort() {
105 _this3._dequeue(handler);
106 },
107 done: function done() {
108 throw new Error('Cannot mark a queued request as done: this indicates a bug');
109 }
110 };
111 var index = findIndex(this.queuedHandlers, function (other) {
112 return handler.priority > other.priority;
113 });
114
115 if (index === -1) {
116 this.queuedHandlers.push(handler);
117 } else {
118 this.queuedHandlers.splice(index, 0, handler);
119 }
120
121 return handler;
122 };
123
124 _proto._dequeue = function _dequeue(handler) {
125 var index = this.queuedHandlers.indexOf(handler);
126
127 if (index !== -1) {
128 this.queuedHandlers.splice(index, 1);
129 }
130 };
131
132 _proto.run = function run(fn, queueOptions) {
133 if (this.activeRequests < this.limit) {
134 return this._call(fn);
135 }
136
137 return this._queue(fn, queueOptions);
138 };
139
140 _proto.wrapPromiseFunction = function wrapPromiseFunction(fn, queueOptions) {
141 var _this4 = this;
142
143 return function () {
144 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
145 args[_key] = arguments[_key];
146 }
147
148 var queuedRequest;
149 var outerPromise = new Promise(function (resolve, reject) {
150 queuedRequest = _this4.run(function () {
151 var cancelError;
152 var innerPromise;
153
154 try {
155 innerPromise = Promise.resolve(fn.apply(void 0, args));
156 } catch (err) {
157 innerPromise = Promise.reject(err);
158 }
159
160 innerPromise.then(function (result) {
161 if (cancelError) {
162 reject(cancelError);
163 } else {
164 queuedRequest.done();
165 resolve(result);
166 }
167 }, function (err) {
168 if (cancelError) {
169 reject(cancelError);
170 } else {
171 queuedRequest.done();
172 reject(err);
173 }
174 });
175 return function () {
176 cancelError = createCancelError();
177 };
178 }, queueOptions);
179 });
180
181 outerPromise.abort = function () {
182 queuedRequest.abort();
183 };
184
185 return outerPromise;
186 };
187 };
188
189 return RateLimitedQueue;
190}();
\No newline at end of file