RateLimitedQueue.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.internalRateLimitedQueue = exports.RateLimitedQueue = void 0;
  6. function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
  7. var id = 0;
  8. function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
  9. function createCancelError() {
  10. return new Error('Cancelled');
  11. }
  12. var _activeRequests = /*#__PURE__*/_classPrivateFieldLooseKey("activeRequests");
  13. var _queuedHandlers = /*#__PURE__*/_classPrivateFieldLooseKey("queuedHandlers");
  14. var _paused = /*#__PURE__*/_classPrivateFieldLooseKey("paused");
  15. var _pauseTimer = /*#__PURE__*/_classPrivateFieldLooseKey("pauseTimer");
  16. var _downLimit = /*#__PURE__*/_classPrivateFieldLooseKey("downLimit");
  17. var _upperLimit = /*#__PURE__*/_classPrivateFieldLooseKey("upperLimit");
  18. var _rateLimitingTimer = /*#__PURE__*/_classPrivateFieldLooseKey("rateLimitingTimer");
  19. var _call = /*#__PURE__*/_classPrivateFieldLooseKey("call");
  20. var _queueNext = /*#__PURE__*/_classPrivateFieldLooseKey("queueNext");
  21. var _next = /*#__PURE__*/_classPrivateFieldLooseKey("next");
  22. var _queue = /*#__PURE__*/_classPrivateFieldLooseKey("queue");
  23. var _dequeue = /*#__PURE__*/_classPrivateFieldLooseKey("dequeue");
  24. var _resume = /*#__PURE__*/_classPrivateFieldLooseKey("resume");
  25. var _increaseLimit = /*#__PURE__*/_classPrivateFieldLooseKey("increaseLimit");
  26. class RateLimitedQueue {
  27. constructor(limit) {
  28. Object.defineProperty(this, _dequeue, {
  29. value: _dequeue2
  30. });
  31. Object.defineProperty(this, _queue, {
  32. value: _queue2
  33. });
  34. Object.defineProperty(this, _next, {
  35. value: _next2
  36. });
  37. Object.defineProperty(this, _queueNext, {
  38. value: _queueNext2
  39. });
  40. Object.defineProperty(this, _call, {
  41. value: _call2
  42. });
  43. Object.defineProperty(this, _activeRequests, {
  44. writable: true,
  45. value: 0
  46. });
  47. Object.defineProperty(this, _queuedHandlers, {
  48. writable: true,
  49. value: []
  50. });
  51. Object.defineProperty(this, _paused, {
  52. writable: true,
  53. value: false
  54. });
  55. Object.defineProperty(this, _pauseTimer, {
  56. writable: true,
  57. value: void 0
  58. });
  59. Object.defineProperty(this, _downLimit, {
  60. writable: true,
  61. value: 1
  62. });
  63. Object.defineProperty(this, _upperLimit, {
  64. writable: true,
  65. value: void 0
  66. });
  67. Object.defineProperty(this, _rateLimitingTimer, {
  68. writable: true,
  69. value: void 0
  70. });
  71. Object.defineProperty(this, _resume, {
  72. writable: true,
  73. value: () => this.resume()
  74. });
  75. Object.defineProperty(this, _increaseLimit, {
  76. writable: true,
  77. value: () => {
  78. if (_classPrivateFieldLooseBase(this, _paused)[_paused]) {
  79. _classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], 0);
  80. return;
  81. }
  82. _classPrivateFieldLooseBase(this, _downLimit)[_downLimit] = this.limit;
  83. this.limit = Math.ceil((_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] + _classPrivateFieldLooseBase(this, _downLimit)[_downLimit]) / 2);
  84. for (let i = _classPrivateFieldLooseBase(this, _downLimit)[_downLimit]; i <= this.limit; i++) {
  85. _classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
  86. }
  87. if (_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] - _classPrivateFieldLooseBase(this, _downLimit)[_downLimit] > 3) {
  88. _classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], 2000);
  89. } else {
  90. _classPrivateFieldLooseBase(this, _downLimit)[_downLimit] = Math.floor(_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] / 2);
  91. }
  92. }
  93. });
  94. if (typeof limit !== 'number' || limit === 0) {
  95. this.limit = Infinity;
  96. } else {
  97. this.limit = limit;
  98. }
  99. }
  100. run(fn, queueOptions) {
  101. if (!_classPrivateFieldLooseBase(this, _paused)[_paused] && _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] < this.limit) {
  102. return _classPrivateFieldLooseBase(this, _call)[_call](fn);
  103. }
  104. return _classPrivateFieldLooseBase(this, _queue)[_queue](fn, queueOptions);
  105. }
  106. wrapPromiseFunction(fn, queueOptions) {
  107. var _this = this;
  108. return function () {
  109. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  110. args[_key] = arguments[_key];
  111. }
  112. let queuedRequest;
  113. const outerPromise = new Promise((resolve, reject) => {
  114. queuedRequest = _this.run(() => {
  115. let cancelError;
  116. let innerPromise;
  117. try {
  118. innerPromise = Promise.resolve(fn(...args));
  119. } catch (err) {
  120. innerPromise = Promise.reject(err);
  121. }
  122. innerPromise.then(result => {
  123. if (cancelError) {
  124. reject(cancelError);
  125. } else {
  126. queuedRequest.done();
  127. resolve(result);
  128. }
  129. }, err => {
  130. if (cancelError) {
  131. reject(cancelError);
  132. } else {
  133. queuedRequest.done();
  134. reject(err);
  135. }
  136. });
  137. return () => {
  138. cancelError = createCancelError();
  139. };
  140. }, queueOptions);
  141. });
  142. outerPromise.abort = () => {
  143. queuedRequest.abort();
  144. };
  145. return outerPromise;
  146. };
  147. }
  148. resume() {
  149. _classPrivateFieldLooseBase(this, _paused)[_paused] = false;
  150. clearTimeout(_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer]);
  151. for (let i = 0; i < this.limit; i++) {
  152. _classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
  153. }
  154. }
  155. /**
  156. * Freezes the queue for a while or indefinitely.
  157. *
  158. * @param {number | null } [duration] Duration for the pause to happen, in milliseconds.
  159. * If omitted, the queue won't resume automatically.
  160. */
  161. pause(duration) {
  162. if (duration === void 0) {
  163. duration = null;
  164. }
  165. _classPrivateFieldLooseBase(this, _paused)[_paused] = true;
  166. clearTimeout(_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer]);
  167. if (duration != null) {
  168. _classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer] = setTimeout(_classPrivateFieldLooseBase(this, _resume)[_resume], duration);
  169. }
  170. }
  171. /**
  172. * Pauses the queue for a duration, and lower the limit of concurrent requests
  173. * when the queue resumes. When the queue resumes, it tries to progressively
  174. * increase the limit in `this.#increaseLimit` until another call is made to
  175. * `this.rateLimit`.
  176. * Call this function when using the RateLimitedQueue for network requests and
  177. * the remote server responds with 429 HTTP code.
  178. *
  179. * @param {number} duration in milliseconds.
  180. */
  181. rateLimit(duration) {
  182. clearTimeout(_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer]);
  183. this.pause(duration);
  184. if (this.limit > 1 && Number.isFinite(this.limit)) {
  185. _classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] = this.limit - 1;
  186. this.limit = _classPrivateFieldLooseBase(this, _downLimit)[_downLimit];
  187. _classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], duration);
  188. }
  189. }
  190. get isPaused() {
  191. return _classPrivateFieldLooseBase(this, _paused)[_paused];
  192. }
  193. }
  194. exports.RateLimitedQueue = RateLimitedQueue;
  195. function _call2(fn) {
  196. _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] += 1;
  197. let done = false;
  198. let cancelActive;
  199. try {
  200. cancelActive = fn();
  201. } catch (err) {
  202. _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
  203. throw err;
  204. }
  205. return {
  206. abort: () => {
  207. if (done) return;
  208. done = true;
  209. _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
  210. cancelActive();
  211. _classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
  212. },
  213. done: () => {
  214. if (done) return;
  215. done = true;
  216. _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
  217. _classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
  218. }
  219. };
  220. }
  221. function _queueNext2() {
  222. // Do it soon but not immediately, this allows clearing out the entire queue synchronously
  223. // one by one without continuously _advancing_ it (and starting new tasks before immediately
  224. // aborting them)
  225. queueMicrotask(() => _classPrivateFieldLooseBase(this, _next)[_next]());
  226. }
  227. function _next2() {
  228. if (_classPrivateFieldLooseBase(this, _paused)[_paused] || _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] >= this.limit) {
  229. return;
  230. }
  231. if (_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].length === 0) {
  232. return;
  233. } // Dispatch the next request, and update the abort/done handlers
  234. // so that cancelling it does the Right Thing (and doesn't just try
  235. // to dequeue an already-running request).
  236. const next = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].shift();
  237. const handler = _classPrivateFieldLooseBase(this, _call)[_call](next.fn);
  238. next.abort = handler.abort;
  239. next.done = handler.done;
  240. }
  241. function _queue2(fn, options) {
  242. if (options === void 0) {
  243. options = {};
  244. }
  245. const handler = {
  246. fn,
  247. priority: options.priority || 0,
  248. abort: () => {
  249. _classPrivateFieldLooseBase(this, _dequeue)[_dequeue](handler);
  250. },
  251. done: () => {
  252. throw new Error('Cannot mark a queued request as done: this indicates a bug');
  253. }
  254. };
  255. const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].findIndex(other => {
  256. return handler.priority > other.priority;
  257. });
  258. if (index === -1) {
  259. _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].push(handler);
  260. } else {
  261. _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 0, handler);
  262. }
  263. return handler;
  264. }
  265. function _dequeue2(handler) {
  266. const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].indexOf(handler);
  267. if (index !== -1) {
  268. _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 1);
  269. }
  270. }
  271. const internalRateLimitedQueue = Symbol('__queue');
  272. exports.internalRateLimitedQueue = internalRateLimitedQueue;