@sapphire/async-queue 1.5.1-next.3bbaad5.0 → 1.5.1-next.3bc31a55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- ![Sapphire Logo](https://cdn.skyra.pw/gh-assets/sapphire-banner.png)
3
+ ![Sapphire Logo](https://raw.githubusercontent.com/sapphiredev/assets/main/banners/SapphireCommunity.png)
4
4
 
5
5
  # @sapphire/async-queue
6
6
 
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The AsyncQueue class used to sequentialize burst requests
3
+ */
4
+ declare class AsyncQueue {
5
+ /**
6
+ * The amount of entries in the queue, including the head.
7
+ * @seealso {@link queued} for the queued count.
8
+ */
9
+ get remaining(): number;
10
+ /**
11
+ * The amount of queued entries.
12
+ * @seealso {@link remaining} for the count with the head.
13
+ */
14
+ get queued(): number;
15
+ /**
16
+ * The promises array
17
+ */
18
+ private promises;
19
+ /**
20
+ * Waits for last promise and queues a new one
21
+ * @example
22
+ * ```typescript
23
+ * const queue = new AsyncQueue();
24
+ * async function request(url, options) {
25
+ * await queue.wait({ signal: options.signal });
26
+ * try {
27
+ * const result = await fetch(url, options);
28
+ * // Do some operations with 'result'
29
+ * } finally {
30
+ * // Remove first entry from the queue and resolve for the next entry
31
+ * queue.shift();
32
+ * }
33
+ * }
34
+ *
35
+ * request(someUrl1, someOptions1); // Will call fetch() immediately
36
+ * request(someUrl2, someOptions2); // Will call fetch() after the first finished
37
+ * request(someUrl3, someOptions3); // Will call fetch() after the second finished
38
+ * ```
39
+ */
40
+ wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void>;
41
+ /**
42
+ * Unlocks the head lock and transfers the next lock (if any) to the head.
43
+ */
44
+ shift(): void;
45
+ /**
46
+ * Aborts all the pending promises.
47
+ * @note To avoid race conditions, this does **not** unlock the head lock.
48
+ */
49
+ abortAll(): void;
50
+ }
51
+ interface AsyncQueueWaitOptions {
52
+ signal?: AbortSignal | undefined | null;
53
+ }
54
+
55
+ export { AsyncQueue, AsyncQueueWaitOptions };
@@ -10,7 +10,7 @@ var SapphireAsyncQueue = (function (exports) {
10
10
  };
11
11
 
12
12
  // src/lib/AsyncQueueEntry.ts
13
- var AsyncQueueEntry = class {
13
+ var _AsyncQueueEntry = class _AsyncQueueEntry {
14
14
  constructor(queue) {
15
15
  __publicField(this, "promise");
16
16
  __publicField(this, "resolve");
@@ -55,19 +55,52 @@ var SapphireAsyncQueue = (function (exports) {
55
55
  }
56
56
  }
57
57
  };
58
- __name(AsyncQueueEntry, "AsyncQueueEntry");
58
+ __name(_AsyncQueueEntry, "AsyncQueueEntry");
59
+ var AsyncQueueEntry = _AsyncQueueEntry;
59
60
 
60
61
  // src/lib/AsyncQueue.ts
61
- var AsyncQueue = class {
62
+ var _AsyncQueue = class _AsyncQueue {
62
63
  constructor() {
64
+ /**
65
+ * The promises array
66
+ */
63
67
  __publicField(this, "promises", []);
64
68
  }
69
+ /**
70
+ * The amount of entries in the queue, including the head.
71
+ * @seealso {@link queued} for the queued count.
72
+ */
65
73
  get remaining() {
66
74
  return this.promises.length;
67
75
  }
76
+ /**
77
+ * The amount of queued entries.
78
+ * @seealso {@link remaining} for the count with the head.
79
+ */
68
80
  get queued() {
69
81
  return this.remaining === 0 ? 0 : this.remaining - 1;
70
82
  }
83
+ /**
84
+ * Waits for last promise and queues a new one
85
+ * @example
86
+ * ```typescript
87
+ * const queue = new AsyncQueue();
88
+ * async function request(url, options) {
89
+ * await queue.wait({ signal: options.signal });
90
+ * try {
91
+ * const result = await fetch(url, options);
92
+ * // Do some operations with 'result'
93
+ * } finally {
94
+ * // Remove first entry from the queue and resolve for the next entry
95
+ * queue.shift();
96
+ * }
97
+ * }
98
+ *
99
+ * request(someUrl1, someOptions1); // Will call fetch() immediately
100
+ * request(someUrl2, someOptions2); // Will call fetch() after the first finished
101
+ * request(someUrl3, someOptions3); // Will call fetch() after the second finished
102
+ * ```
103
+ */
71
104
  wait(options) {
72
105
  const entry = new AsyncQueueEntry(this);
73
106
  if (this.promises.length === 0) {
@@ -79,6 +112,9 @@ var SapphireAsyncQueue = (function (exports) {
79
112
  entry.setSignal(options.signal);
80
113
  return entry.promise;
81
114
  }
115
+ /**
116
+ * Unlocks the head lock and transfers the next lock (if any) to the head.
117
+ */
82
118
  shift() {
83
119
  if (this.promises.length === 0)
84
120
  return;
@@ -89,6 +125,10 @@ var SapphireAsyncQueue = (function (exports) {
89
125
  this.promises.shift();
90
126
  this.promises[0].use();
91
127
  }
128
+ /**
129
+ * Aborts all the pending promises.
130
+ * @note To avoid race conditions, this does **not** unlock the head lock.
131
+ */
92
132
  abortAll() {
93
133
  if (this.queued === 0)
94
134
  return;
@@ -98,7 +138,8 @@ var SapphireAsyncQueue = (function (exports) {
98
138
  this.promises.length = 1;
99
139
  }
100
140
  };
101
- __name(AsyncQueue, "AsyncQueue");
141
+ __name(_AsyncQueue, "AsyncQueue");
142
+ var AsyncQueue = _AsyncQueue;
102
143
 
103
144
  exports.AsyncQueue = AsyncQueue;
104
145
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/AsyncQueueEntry.ts","../src/lib/AsyncQueue.ts"],"names":[],"mappings":";;;;;;;;;AAKO,IAAM,kBAAN,MAAsB;AAAA,EAQrB,YAAY,OAAmB;AAPtC,wBAAgB;AAChB,wBAAQ;AACR,wBAAQ;AACR,wBAAiB;AACjB,wBAAQ,UAAqC;AAC7C,wBAAQ,kBAAsC;AAG7C,SAAK,QAAQ;AACb,SAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IACf,CAAC;AAAA,EACF;AAAA,EAEO,UAAU,QAAqB;AACrC,QAAI,OAAO;AAAS,aAAO;AAE3B,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC3B,YAAM,QAAQ,KAAK,MAAM,YAAY,QAAQ,IAAI;AACjD,UAAI,UAAU;AAAI,aAAK,MAAM,YAAY,OAAO,OAAO,CAAC;AAExD,WAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,IAClD;AACA,SAAK,OAAO,iBAAiB,SAAS,KAAK,cAAc;AACzD,WAAO;AAAA,EACR;AAAA,EAEO,MAAM;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EAEO,QAAQ;AACd,SAAK,QAAQ;AACb,SAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACjD,WAAO;AAAA,EACR;AAAA,EAEQ,UAAU;AACjB,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,oBAAoB,SAAS,KAAK,cAAe;AAC7D,WAAK,SAAS;AACd,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AACD;AAjDa;;;ACAN,IAAM,aAAN,MAAiB;AAAA,EAAjB;AAoBN,wBAAQ,YAA8B,CAAC;AAAA;AAAA,EAfvC,IAAW,YAAoB;AAC9B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA,EAMA,IAAW,SAAiB;AAC3B,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY;AAAA,EACpD;AAAA,EA4BO,KAAK,SAA0D;AACrE,UAAM,QAAQ,IAAI,gBAAgB,IAAI;AAEtC,QAAI,KAAK,SAAS,WAAW,GAAG;AAC/B,WAAK,SAAS,KAAK,KAAK;AACxB,aAAO,QAAQ,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,KAAK,KAAK;AACxB,QAAI,SAAS;AAAQ,YAAM,UAAU,QAAQ,MAAM;AACnD,WAAO,MAAM;AAAA,EACd;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,SAAS,WAAW;AAAG;AAChC,QAAI,KAAK,SAAS,WAAW,GAAG;AAE/B,WAAK,SAAS,MAAM;AACpB;AAAA,IACD;AAIA,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,GAAG,IAAI;AAAA,EACtB;AAAA,EAMO,WAAiB;AAEvB,QAAI,KAAK,WAAW;AAAG;AAIvB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,EAAE,GAAG;AAC9C,WAAK,SAAS,GAAG,MAAM;AAAA,IACxB;AAEA,SAAK,SAAS,SAAS;AAAA,EACxB;AACD;AAzFa","sourcesContent":["import type { AsyncQueue } from './AsyncQueue';\n\n/**\n * @internal\n */\nexport class AsyncQueueEntry {\n\tpublic readonly promise: Promise<void>;\n\tprivate resolve!: () => void;\n\tprivate reject!: (error: Error) => void;\n\tprivate readonly queue: AsyncQueue;\n\tprivate signal: PolyFillAbortSignal | null = null;\n\tprivate signalListener: (() => void) | null = null;\n\n\tpublic constructor(queue: AsyncQueue) {\n\t\tthis.queue = queue;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tthis.resolve = resolve;\n\t\t\tthis.reject = reject;\n\t\t});\n\t}\n\n\tpublic setSignal(signal: AbortSignal) {\n\t\tif (signal.aborted) return this;\n\n\t\tthis.signal = signal as PolyFillAbortSignal;\n\t\tthis.signalListener = () => {\n\t\t\tconst index = this.queue['promises'].indexOf(this);\n\t\t\tif (index !== -1) this.queue['promises'].splice(index, 1);\n\n\t\t\tthis.reject(new Error('Request aborted manually'));\n\t\t};\n\t\tthis.signal.addEventListener('abort', this.signalListener);\n\t\treturn this;\n\t}\n\n\tpublic use() {\n\t\tthis.dispose();\n\t\tthis.resolve();\n\t\treturn this;\n\t}\n\n\tpublic abort() {\n\t\tthis.dispose();\n\t\tthis.reject(new Error('Request aborted manually'));\n\t\treturn this;\n\t}\n\n\tprivate dispose() {\n\t\tif (this.signal) {\n\t\t\tthis.signal.removeEventListener('abort', this.signalListener!);\n\t\t\tthis.signal = null;\n\t\t\tthis.signalListener = null;\n\t\t}\n\t}\n}\n\ninterface PolyFillAbortSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: 'abort', listener: () => void): void;\n\tremoveEventListener(type: 'abort', listener: () => void): void;\n}\n","import { AsyncQueueEntry } from './AsyncQueueEntry';\n\n/**\n * The AsyncQueue class used to sequentialize burst requests\n */\nexport class AsyncQueue {\n\t/**\n\t * The amount of entries in the queue, including the head.\n\t * @seealso {@link queued} for the queued count.\n\t */\n\tpublic get remaining(): number {\n\t\treturn this.promises.length;\n\t}\n\n\t/**\n\t * The amount of queued entries.\n\t * @seealso {@link remaining} for the count with the head.\n\t */\n\tpublic get queued(): number {\n\t\treturn this.remaining === 0 ? 0 : this.remaining - 1;\n\t}\n\n\t/**\n\t * The promises array\n\t */\n\tprivate promises: AsyncQueueEntry[] = [];\n\n\t/**\n\t * Waits for last promise and queues a new one\n\t * @example\n\t * ```typescript\n\t * const queue = new AsyncQueue();\n\t * async function request(url, options) {\n\t * await queue.wait({ signal: options.signal });\n\t * try {\n\t * const result = await fetch(url, options);\n\t * // Do some operations with 'result'\n\t * } finally {\n\t * // Remove first entry from the queue and resolve for the next entry\n\t * queue.shift();\n\t * }\n\t * }\n\t *\n\t * request(someUrl1, someOptions1); // Will call fetch() immediately\n\t * request(someUrl2, someOptions2); // Will call fetch() after the first finished\n\t * request(someUrl3, someOptions3); // Will call fetch() after the second finished\n\t * ```\n\t */\n\tpublic wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void> {\n\t\tconst entry = new AsyncQueueEntry(this);\n\n\t\tif (this.promises.length === 0) {\n\t\t\tthis.promises.push(entry);\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tthis.promises.push(entry);\n\t\tif (options?.signal) entry.setSignal(options.signal);\n\t\treturn entry.promise;\n\t}\n\n\t/**\n\t * Unlocks the head lock and transfers the next lock (if any) to the head.\n\t */\n\tpublic shift(): void {\n\t\tif (this.promises.length === 0) return;\n\t\tif (this.promises.length === 1) {\n\t\t\t// Remove the head entry.\n\t\t\tthis.promises.shift();\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the head entry, making the 2nd entry the new one.\n\t\t// Then use the head entry, which will unlock the promise.\n\t\tthis.promises.shift();\n\t\tthis.promises[0].use();\n\t}\n\n\t/**\n\t * Aborts all the pending promises.\n\t * @note To avoid race conditions, this does **not** unlock the head lock.\n\t */\n\tpublic abortAll(): void {\n\t\t// If there are no queued entries, skip early.\n\t\tif (this.queued === 0) return;\n\n\t\t// Abort all the entries except the head, that is why the loop starts at\n\t\t// 1 and not at 0.\n\t\tfor (let i = 1; i < this.promises.length; ++i) {\n\t\t\tthis.promises[i].abort();\n\t\t}\n\n\t\tthis.promises.length = 1;\n\t}\n}\n\nexport interface AsyncQueueWaitOptions {\n\tsignal?: AbortSignal | undefined | null;\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/AsyncQueueEntry.ts","../src/lib/AsyncQueue.ts"],"names":[],"mappings":";;;;;;;;;AAKO,IAAM,mBAAN,MAAM,iBAAgB;AAAA,EAQrB,YAAY,OAAmB;AAPtC,wBAAgB;AAChB,wBAAQ;AACR,wBAAQ;AACR,wBAAiB;AACjB,wBAAQ,UAAqC;AAC7C,wBAAQ,kBAAsC;AAG7C,SAAK,QAAQ;AACb,SAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IACf,CAAC;AAAA,EACF;AAAA,EAEO,UAAU,QAAqB;AACrC,QAAI,OAAO;AAAS,aAAO;AAE3B,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC3B,YAAM,QAAQ,KAAK,MAAM,UAAU,EAAE,QAAQ,IAAI;AACjD,UAAI,UAAU;AAAI,aAAK,MAAM,UAAU,EAAE,OAAO,OAAO,CAAC;AAExD,WAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,IAClD;AACA,SAAK,OAAO,iBAAiB,SAAS,KAAK,cAAc;AACzD,WAAO;AAAA,EACR;AAAA,EAEO,MAAM;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EAEO,QAAQ;AACd,SAAK,QAAQ;AACb,SAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACjD,WAAO;AAAA,EACR;AAAA,EAEQ,UAAU;AACjB,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,oBAAoB,SAAS,KAAK,cAAe;AAC7D,WAAK,SAAS;AACd,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AACD;AAjD6B;AAAtB,IAAM,kBAAN;;;ACAA,IAAM,cAAN,MAAM,YAAW;AAAA,EAAjB;AAoBN;AAAA;AAAA;AAAA,wBAAQ,YAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAfvC,IAAW,YAAoB;AAC9B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAW,SAAiB;AAC3B,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BO,KAAK,SAA0D;AACrE,UAAM,QAAQ,IAAI,gBAAgB,IAAI;AAEtC,QAAI,KAAK,SAAS,WAAW,GAAG;AAC/B,WAAK,SAAS,KAAK,KAAK;AACxB,aAAO,QAAQ,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,KAAK,KAAK;AACxB,QAAI,SAAS;AAAQ,YAAM,UAAU,QAAQ,MAAM;AACnD,WAAO,MAAM;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,SAAS,WAAW;AAAG;AAChC,QAAI,KAAK,SAAS,WAAW,GAAG;AAE/B,WAAK,SAAS,MAAM;AACpB;AAAA,IACD;AAIA,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,CAAC,EAAE,IAAI;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,WAAiB;AAEvB,QAAI,KAAK,WAAW;AAAG;AAIvB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,EAAE,GAAG;AAC9C,WAAK,SAAS,CAAC,EAAE,MAAM;AAAA,IACxB;AAEA,SAAK,SAAS,SAAS;AAAA,EACxB;AACD;AAzFwB;AAAjB,IAAM,aAAN","sourcesContent":["import type { AsyncQueue } from './AsyncQueue';\n\n/**\n * @internal\n */\nexport class AsyncQueueEntry {\n\tpublic readonly promise: Promise<void>;\n\tprivate resolve!: () => void;\n\tprivate reject!: (error: Error) => void;\n\tprivate readonly queue: AsyncQueue;\n\tprivate signal: PolyFillAbortSignal | null = null;\n\tprivate signalListener: (() => void) | null = null;\n\n\tpublic constructor(queue: AsyncQueue) {\n\t\tthis.queue = queue;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tthis.resolve = resolve;\n\t\t\tthis.reject = reject;\n\t\t});\n\t}\n\n\tpublic setSignal(signal: AbortSignal) {\n\t\tif (signal.aborted) return this;\n\n\t\tthis.signal = signal as PolyFillAbortSignal;\n\t\tthis.signalListener = () => {\n\t\t\tconst index = this.queue['promises'].indexOf(this);\n\t\t\tif (index !== -1) this.queue['promises'].splice(index, 1);\n\n\t\t\tthis.reject(new Error('Request aborted manually'));\n\t\t};\n\t\tthis.signal.addEventListener('abort', this.signalListener);\n\t\treturn this;\n\t}\n\n\tpublic use() {\n\t\tthis.dispose();\n\t\tthis.resolve();\n\t\treturn this;\n\t}\n\n\tpublic abort() {\n\t\tthis.dispose();\n\t\tthis.reject(new Error('Request aborted manually'));\n\t\treturn this;\n\t}\n\n\tprivate dispose() {\n\t\tif (this.signal) {\n\t\t\tthis.signal.removeEventListener('abort', this.signalListener!);\n\t\t\tthis.signal = null;\n\t\t\tthis.signalListener = null;\n\t\t}\n\t}\n}\n\ninterface PolyFillAbortSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: 'abort', listener: () => void): void;\n\tremoveEventListener(type: 'abort', listener: () => void): void;\n}\n","import { AsyncQueueEntry } from './AsyncQueueEntry';\n\n/**\n * The AsyncQueue class used to sequentialize burst requests\n */\nexport class AsyncQueue {\n\t/**\n\t * The amount of entries in the queue, including the head.\n\t * @seealso {@link queued} for the queued count.\n\t */\n\tpublic get remaining(): number {\n\t\treturn this.promises.length;\n\t}\n\n\t/**\n\t * The amount of queued entries.\n\t * @seealso {@link remaining} for the count with the head.\n\t */\n\tpublic get queued(): number {\n\t\treturn this.remaining === 0 ? 0 : this.remaining - 1;\n\t}\n\n\t/**\n\t * The promises array\n\t */\n\tprivate promises: AsyncQueueEntry[] = [];\n\n\t/**\n\t * Waits for last promise and queues a new one\n\t * @example\n\t * ```typescript\n\t * const queue = new AsyncQueue();\n\t * async function request(url, options) {\n\t * await queue.wait({ signal: options.signal });\n\t * try {\n\t * const result = await fetch(url, options);\n\t * // Do some operations with 'result'\n\t * } finally {\n\t * // Remove first entry from the queue and resolve for the next entry\n\t * queue.shift();\n\t * }\n\t * }\n\t *\n\t * request(someUrl1, someOptions1); // Will call fetch() immediately\n\t * request(someUrl2, someOptions2); // Will call fetch() after the first finished\n\t * request(someUrl3, someOptions3); // Will call fetch() after the second finished\n\t * ```\n\t */\n\tpublic wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void> {\n\t\tconst entry = new AsyncQueueEntry(this);\n\n\t\tif (this.promises.length === 0) {\n\t\t\tthis.promises.push(entry);\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tthis.promises.push(entry);\n\t\tif (options?.signal) entry.setSignal(options.signal);\n\t\treturn entry.promise;\n\t}\n\n\t/**\n\t * Unlocks the head lock and transfers the next lock (if any) to the head.\n\t */\n\tpublic shift(): void {\n\t\tif (this.promises.length === 0) return;\n\t\tif (this.promises.length === 1) {\n\t\t\t// Remove the head entry.\n\t\t\tthis.promises.shift();\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the head entry, making the 2nd entry the new one.\n\t\t// Then use the head entry, which will unlock the promise.\n\t\tthis.promises.shift();\n\t\tthis.promises[0].use();\n\t}\n\n\t/**\n\t * Aborts all the pending promises.\n\t * @note To avoid race conditions, this does **not** unlock the head lock.\n\t */\n\tpublic abortAll(): void {\n\t\t// If there are no queued entries, skip early.\n\t\tif (this.queued === 0) return;\n\n\t\t// Abort all the entries except the head, that is why the loop starts at\n\t\t// 1 and not at 0.\n\t\tfor (let i = 1; i < this.promises.length; ++i) {\n\t\t\tthis.promises[i].abort();\n\t\t}\n\n\t\tthis.promises.length = 1;\n\t}\n}\n\nexport interface AsyncQueueWaitOptions {\n\tsignal?: AbortSignal | undefined | null;\n}\n"]}
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ var __publicField = (obj, key, value) => {
9
9
  };
10
10
 
11
11
  // src/lib/AsyncQueueEntry.ts
12
- var AsyncQueueEntry = class {
12
+ var _AsyncQueueEntry = class _AsyncQueueEntry {
13
13
  constructor(queue) {
14
14
  __publicField(this, "promise");
15
15
  __publicField(this, "resolve");
@@ -54,19 +54,52 @@ var AsyncQueueEntry = class {
54
54
  }
55
55
  }
56
56
  };
57
- __name(AsyncQueueEntry, "AsyncQueueEntry");
57
+ __name(_AsyncQueueEntry, "AsyncQueueEntry");
58
+ var AsyncQueueEntry = _AsyncQueueEntry;
58
59
 
59
60
  // src/lib/AsyncQueue.ts
60
- var AsyncQueue = class {
61
+ var _AsyncQueue = class _AsyncQueue {
61
62
  constructor() {
63
+ /**
64
+ * The promises array
65
+ */
62
66
  __publicField(this, "promises", []);
63
67
  }
68
+ /**
69
+ * The amount of entries in the queue, including the head.
70
+ * @seealso {@link queued} for the queued count.
71
+ */
64
72
  get remaining() {
65
73
  return this.promises.length;
66
74
  }
75
+ /**
76
+ * The amount of queued entries.
77
+ * @seealso {@link remaining} for the count with the head.
78
+ */
67
79
  get queued() {
68
80
  return this.remaining === 0 ? 0 : this.remaining - 1;
69
81
  }
82
+ /**
83
+ * Waits for last promise and queues a new one
84
+ * @example
85
+ * ```typescript
86
+ * const queue = new AsyncQueue();
87
+ * async function request(url, options) {
88
+ * await queue.wait({ signal: options.signal });
89
+ * try {
90
+ * const result = await fetch(url, options);
91
+ * // Do some operations with 'result'
92
+ * } finally {
93
+ * // Remove first entry from the queue and resolve for the next entry
94
+ * queue.shift();
95
+ * }
96
+ * }
97
+ *
98
+ * request(someUrl1, someOptions1); // Will call fetch() immediately
99
+ * request(someUrl2, someOptions2); // Will call fetch() after the first finished
100
+ * request(someUrl3, someOptions3); // Will call fetch() after the second finished
101
+ * ```
102
+ */
70
103
  wait(options) {
71
104
  const entry = new AsyncQueueEntry(this);
72
105
  if (this.promises.length === 0) {
@@ -78,6 +111,9 @@ var AsyncQueue = class {
78
111
  entry.setSignal(options.signal);
79
112
  return entry.promise;
80
113
  }
114
+ /**
115
+ * Unlocks the head lock and transfers the next lock (if any) to the head.
116
+ */
81
117
  shift() {
82
118
  if (this.promises.length === 0)
83
119
  return;
@@ -88,6 +124,10 @@ var AsyncQueue = class {
88
124
  this.promises.shift();
89
125
  this.promises[0].use();
90
126
  }
127
+ /**
128
+ * Aborts all the pending promises.
129
+ * @note To avoid race conditions, this does **not** unlock the head lock.
130
+ */
91
131
  abortAll() {
92
132
  if (this.queued === 0)
93
133
  return;
@@ -97,7 +137,8 @@ var AsyncQueue = class {
97
137
  this.promises.length = 1;
98
138
  }
99
139
  };
100
- __name(AsyncQueue, "AsyncQueue");
140
+ __name(_AsyncQueue, "AsyncQueue");
141
+ var AsyncQueue = _AsyncQueue;
101
142
 
102
143
  exports.AsyncQueue = AsyncQueue;
103
144
  //# sourceMappingURL=out.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/AsyncQueueEntry.ts","../src/lib/AsyncQueue.ts"],"names":[],"mappings":";;;;;;;;;AAKO,IAAM,kBAAN,MAAsB;AAAA,EAQrB,YAAY,OAAmB;AAPtC,wBAAgB;AAChB,wBAAQ;AACR,wBAAQ;AACR,wBAAiB;AACjB,wBAAQ,UAAqC;AAC7C,wBAAQ,kBAAsC;AAG7C,SAAK,QAAQ;AACb,SAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IACf,CAAC;AAAA,EACF;AAAA,EAEO,UAAU,QAAqB;AACrC,QAAI,OAAO;AAAS,aAAO;AAE3B,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC3B,YAAM,QAAQ,KAAK,MAAM,YAAY,QAAQ,IAAI;AACjD,UAAI,UAAU;AAAI,aAAK,MAAM,YAAY,OAAO,OAAO,CAAC;AAExD,WAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,IAClD;AACA,SAAK,OAAO,iBAAiB,SAAS,KAAK,cAAc;AACzD,WAAO;AAAA,EACR;AAAA,EAEO,MAAM;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EAEO,QAAQ;AACd,SAAK,QAAQ;AACb,SAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACjD,WAAO;AAAA,EACR;AAAA,EAEQ,UAAU;AACjB,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,oBAAoB,SAAS,KAAK,cAAe;AAC7D,WAAK,SAAS;AACd,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AACD;AAjDa;;;ACAN,IAAM,aAAN,MAAiB;AAAA,EAAjB;AAoBN,wBAAQ,YAA8B,CAAC;AAAA;AAAA,EAfvC,IAAW,YAAoB;AAC9B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA,EAMA,IAAW,SAAiB;AAC3B,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY;AAAA,EACpD;AAAA,EA4BO,KAAK,SAA0D;AACrE,UAAM,QAAQ,IAAI,gBAAgB,IAAI;AAEtC,QAAI,KAAK,SAAS,WAAW,GAAG;AAC/B,WAAK,SAAS,KAAK,KAAK;AACxB,aAAO,QAAQ,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,KAAK,KAAK;AACxB,QAAI,SAAS;AAAQ,YAAM,UAAU,QAAQ,MAAM;AACnD,WAAO,MAAM;AAAA,EACd;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,SAAS,WAAW;AAAG;AAChC,QAAI,KAAK,SAAS,WAAW,GAAG;AAE/B,WAAK,SAAS,MAAM;AACpB;AAAA,IACD;AAIA,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,GAAG,IAAI;AAAA,EACtB;AAAA,EAMO,WAAiB;AAEvB,QAAI,KAAK,WAAW;AAAG;AAIvB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,EAAE,GAAG;AAC9C,WAAK,SAAS,GAAG,MAAM;AAAA,IACxB;AAEA,SAAK,SAAS,SAAS;AAAA,EACxB;AACD;AAzFa","sourcesContent":["import type { AsyncQueue } from './AsyncQueue';\n\n/**\n * @internal\n */\nexport class AsyncQueueEntry {\n\tpublic readonly promise: Promise<void>;\n\tprivate resolve!: () => void;\n\tprivate reject!: (error: Error) => void;\n\tprivate readonly queue: AsyncQueue;\n\tprivate signal: PolyFillAbortSignal | null = null;\n\tprivate signalListener: (() => void) | null = null;\n\n\tpublic constructor(queue: AsyncQueue) {\n\t\tthis.queue = queue;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tthis.resolve = resolve;\n\t\t\tthis.reject = reject;\n\t\t});\n\t}\n\n\tpublic setSignal(signal: AbortSignal) {\n\t\tif (signal.aborted) return this;\n\n\t\tthis.signal = signal as PolyFillAbortSignal;\n\t\tthis.signalListener = () => {\n\t\t\tconst index = this.queue['promises'].indexOf(this);\n\t\t\tif (index !== -1) this.queue['promises'].splice(index, 1);\n\n\t\t\tthis.reject(new Error('Request aborted manually'));\n\t\t};\n\t\tthis.signal.addEventListener('abort', this.signalListener);\n\t\treturn this;\n\t}\n\n\tpublic use() {\n\t\tthis.dispose();\n\t\tthis.resolve();\n\t\treturn this;\n\t}\n\n\tpublic abort() {\n\t\tthis.dispose();\n\t\tthis.reject(new Error('Request aborted manually'));\n\t\treturn this;\n\t}\n\n\tprivate dispose() {\n\t\tif (this.signal) {\n\t\t\tthis.signal.removeEventListener('abort', this.signalListener!);\n\t\t\tthis.signal = null;\n\t\t\tthis.signalListener = null;\n\t\t}\n\t}\n}\n\ninterface PolyFillAbortSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: 'abort', listener: () => void): void;\n\tremoveEventListener(type: 'abort', listener: () => void): void;\n}\n","import { AsyncQueueEntry } from './AsyncQueueEntry';\n\n/**\n * The AsyncQueue class used to sequentialize burst requests\n */\nexport class AsyncQueue {\n\t/**\n\t * The amount of entries in the queue, including the head.\n\t * @seealso {@link queued} for the queued count.\n\t */\n\tpublic get remaining(): number {\n\t\treturn this.promises.length;\n\t}\n\n\t/**\n\t * The amount of queued entries.\n\t * @seealso {@link remaining} for the count with the head.\n\t */\n\tpublic get queued(): number {\n\t\treturn this.remaining === 0 ? 0 : this.remaining - 1;\n\t}\n\n\t/**\n\t * The promises array\n\t */\n\tprivate promises: AsyncQueueEntry[] = [];\n\n\t/**\n\t * Waits for last promise and queues a new one\n\t * @example\n\t * ```typescript\n\t * const queue = new AsyncQueue();\n\t * async function request(url, options) {\n\t * await queue.wait({ signal: options.signal });\n\t * try {\n\t * const result = await fetch(url, options);\n\t * // Do some operations with 'result'\n\t * } finally {\n\t * // Remove first entry from the queue and resolve for the next entry\n\t * queue.shift();\n\t * }\n\t * }\n\t *\n\t * request(someUrl1, someOptions1); // Will call fetch() immediately\n\t * request(someUrl2, someOptions2); // Will call fetch() after the first finished\n\t * request(someUrl3, someOptions3); // Will call fetch() after the second finished\n\t * ```\n\t */\n\tpublic wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void> {\n\t\tconst entry = new AsyncQueueEntry(this);\n\n\t\tif (this.promises.length === 0) {\n\t\t\tthis.promises.push(entry);\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tthis.promises.push(entry);\n\t\tif (options?.signal) entry.setSignal(options.signal);\n\t\treturn entry.promise;\n\t}\n\n\t/**\n\t * Unlocks the head lock and transfers the next lock (if any) to the head.\n\t */\n\tpublic shift(): void {\n\t\tif (this.promises.length === 0) return;\n\t\tif (this.promises.length === 1) {\n\t\t\t// Remove the head entry.\n\t\t\tthis.promises.shift();\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the head entry, making the 2nd entry the new one.\n\t\t// Then use the head entry, which will unlock the promise.\n\t\tthis.promises.shift();\n\t\tthis.promises[0].use();\n\t}\n\n\t/**\n\t * Aborts all the pending promises.\n\t * @note To avoid race conditions, this does **not** unlock the head lock.\n\t */\n\tpublic abortAll(): void {\n\t\t// If there are no queued entries, skip early.\n\t\tif (this.queued === 0) return;\n\n\t\t// Abort all the entries except the head, that is why the loop starts at\n\t\t// 1 and not at 0.\n\t\tfor (let i = 1; i < this.promises.length; ++i) {\n\t\t\tthis.promises[i].abort();\n\t\t}\n\n\t\tthis.promises.length = 1;\n\t}\n}\n\nexport interface AsyncQueueWaitOptions {\n\tsignal?: AbortSignal | undefined | null;\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/AsyncQueueEntry.ts","../src/lib/AsyncQueue.ts"],"names":[],"mappings":";;;;;;;;;AAKO,IAAM,mBAAN,MAAM,iBAAgB;AAAA,EAQrB,YAAY,OAAmB;AAPtC,wBAAgB;AAChB,wBAAQ;AACR,wBAAQ;AACR,wBAAiB;AACjB,wBAAQ,UAAqC;AAC7C,wBAAQ,kBAAsC;AAG7C,SAAK,QAAQ;AACb,SAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IACf,CAAC;AAAA,EACF;AAAA,EAEO,UAAU,QAAqB;AACrC,QAAI,OAAO;AAAS,aAAO;AAE3B,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC3B,YAAM,QAAQ,KAAK,MAAM,UAAU,EAAE,QAAQ,IAAI;AACjD,UAAI,UAAU;AAAI,aAAK,MAAM,UAAU,EAAE,OAAO,OAAO,CAAC;AAExD,WAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,IAClD;AACA,SAAK,OAAO,iBAAiB,SAAS,KAAK,cAAc;AACzD,WAAO;AAAA,EACR;AAAA,EAEO,MAAM;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EAEO,QAAQ;AACd,SAAK,QAAQ;AACb,SAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACjD,WAAO;AAAA,EACR;AAAA,EAEQ,UAAU;AACjB,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,oBAAoB,SAAS,KAAK,cAAe;AAC7D,WAAK,SAAS;AACd,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AACD;AAjD6B;AAAtB,IAAM,kBAAN;;;ACAA,IAAM,cAAN,MAAM,YAAW;AAAA,EAAjB;AAoBN;AAAA;AAAA;AAAA,wBAAQ,YAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAfvC,IAAW,YAAoB;AAC9B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAW,SAAiB;AAC3B,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BO,KAAK,SAA0D;AACrE,UAAM,QAAQ,IAAI,gBAAgB,IAAI;AAEtC,QAAI,KAAK,SAAS,WAAW,GAAG;AAC/B,WAAK,SAAS,KAAK,KAAK;AACxB,aAAO,QAAQ,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,KAAK,KAAK;AACxB,QAAI,SAAS;AAAQ,YAAM,UAAU,QAAQ,MAAM;AACnD,WAAO,MAAM;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,SAAS,WAAW;AAAG;AAChC,QAAI,KAAK,SAAS,WAAW,GAAG;AAE/B,WAAK,SAAS,MAAM;AACpB;AAAA,IACD;AAIA,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,CAAC,EAAE,IAAI;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,WAAiB;AAEvB,QAAI,KAAK,WAAW;AAAG;AAIvB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,EAAE,GAAG;AAC9C,WAAK,SAAS,CAAC,EAAE,MAAM;AAAA,IACxB;AAEA,SAAK,SAAS,SAAS;AAAA,EACxB;AACD;AAzFwB;AAAjB,IAAM,aAAN","sourcesContent":["import type { AsyncQueue } from './AsyncQueue';\n\n/**\n * @internal\n */\nexport class AsyncQueueEntry {\n\tpublic readonly promise: Promise<void>;\n\tprivate resolve!: () => void;\n\tprivate reject!: (error: Error) => void;\n\tprivate readonly queue: AsyncQueue;\n\tprivate signal: PolyFillAbortSignal | null = null;\n\tprivate signalListener: (() => void) | null = null;\n\n\tpublic constructor(queue: AsyncQueue) {\n\t\tthis.queue = queue;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tthis.resolve = resolve;\n\t\t\tthis.reject = reject;\n\t\t});\n\t}\n\n\tpublic setSignal(signal: AbortSignal) {\n\t\tif (signal.aborted) return this;\n\n\t\tthis.signal = signal as PolyFillAbortSignal;\n\t\tthis.signalListener = () => {\n\t\t\tconst index = this.queue['promises'].indexOf(this);\n\t\t\tif (index !== -1) this.queue['promises'].splice(index, 1);\n\n\t\t\tthis.reject(new Error('Request aborted manually'));\n\t\t};\n\t\tthis.signal.addEventListener('abort', this.signalListener);\n\t\treturn this;\n\t}\n\n\tpublic use() {\n\t\tthis.dispose();\n\t\tthis.resolve();\n\t\treturn this;\n\t}\n\n\tpublic abort() {\n\t\tthis.dispose();\n\t\tthis.reject(new Error('Request aborted manually'));\n\t\treturn this;\n\t}\n\n\tprivate dispose() {\n\t\tif (this.signal) {\n\t\t\tthis.signal.removeEventListener('abort', this.signalListener!);\n\t\t\tthis.signal = null;\n\t\t\tthis.signalListener = null;\n\t\t}\n\t}\n}\n\ninterface PolyFillAbortSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: 'abort', listener: () => void): void;\n\tremoveEventListener(type: 'abort', listener: () => void): void;\n}\n","import { AsyncQueueEntry } from './AsyncQueueEntry';\n\n/**\n * The AsyncQueue class used to sequentialize burst requests\n */\nexport class AsyncQueue {\n\t/**\n\t * The amount of entries in the queue, including the head.\n\t * @seealso {@link queued} for the queued count.\n\t */\n\tpublic get remaining(): number {\n\t\treturn this.promises.length;\n\t}\n\n\t/**\n\t * The amount of queued entries.\n\t * @seealso {@link remaining} for the count with the head.\n\t */\n\tpublic get queued(): number {\n\t\treturn this.remaining === 0 ? 0 : this.remaining - 1;\n\t}\n\n\t/**\n\t * The promises array\n\t */\n\tprivate promises: AsyncQueueEntry[] = [];\n\n\t/**\n\t * Waits for last promise and queues a new one\n\t * @example\n\t * ```typescript\n\t * const queue = new AsyncQueue();\n\t * async function request(url, options) {\n\t * await queue.wait({ signal: options.signal });\n\t * try {\n\t * const result = await fetch(url, options);\n\t * // Do some operations with 'result'\n\t * } finally {\n\t * // Remove first entry from the queue and resolve for the next entry\n\t * queue.shift();\n\t * }\n\t * }\n\t *\n\t * request(someUrl1, someOptions1); // Will call fetch() immediately\n\t * request(someUrl2, someOptions2); // Will call fetch() after the first finished\n\t * request(someUrl3, someOptions3); // Will call fetch() after the second finished\n\t * ```\n\t */\n\tpublic wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void> {\n\t\tconst entry = new AsyncQueueEntry(this);\n\n\t\tif (this.promises.length === 0) {\n\t\t\tthis.promises.push(entry);\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tthis.promises.push(entry);\n\t\tif (options?.signal) entry.setSignal(options.signal);\n\t\treturn entry.promise;\n\t}\n\n\t/**\n\t * Unlocks the head lock and transfers the next lock (if any) to the head.\n\t */\n\tpublic shift(): void {\n\t\tif (this.promises.length === 0) return;\n\t\tif (this.promises.length === 1) {\n\t\t\t// Remove the head entry.\n\t\t\tthis.promises.shift();\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the head entry, making the 2nd entry the new one.\n\t\t// Then use the head entry, which will unlock the promise.\n\t\tthis.promises.shift();\n\t\tthis.promises[0].use();\n\t}\n\n\t/**\n\t * Aborts all the pending promises.\n\t * @note To avoid race conditions, this does **not** unlock the head lock.\n\t */\n\tpublic abortAll(): void {\n\t\t// If there are no queued entries, skip early.\n\t\tif (this.queued === 0) return;\n\n\t\t// Abort all the entries except the head, that is why the loop starts at\n\t\t// 1 and not at 0.\n\t\tfor (let i = 1; i < this.promises.length; ++i) {\n\t\t\tthis.promises[i].abort();\n\t\t}\n\n\t\tthis.promises.length = 1;\n\t}\n}\n\nexport interface AsyncQueueWaitOptions {\n\tsignal?: AbortSignal | undefined | null;\n}\n"]}
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ var __publicField = (obj, key, value) => {
7
7
  };
8
8
 
9
9
  // src/lib/AsyncQueueEntry.ts
10
- var AsyncQueueEntry = class {
10
+ var _AsyncQueueEntry = class _AsyncQueueEntry {
11
11
  constructor(queue) {
12
12
  __publicField(this, "promise");
13
13
  __publicField(this, "resolve");
@@ -52,19 +52,52 @@ var AsyncQueueEntry = class {
52
52
  }
53
53
  }
54
54
  };
55
- __name(AsyncQueueEntry, "AsyncQueueEntry");
55
+ __name(_AsyncQueueEntry, "AsyncQueueEntry");
56
+ var AsyncQueueEntry = _AsyncQueueEntry;
56
57
 
57
58
  // src/lib/AsyncQueue.ts
58
- var AsyncQueue = class {
59
+ var _AsyncQueue = class _AsyncQueue {
59
60
  constructor() {
61
+ /**
62
+ * The promises array
63
+ */
60
64
  __publicField(this, "promises", []);
61
65
  }
66
+ /**
67
+ * The amount of entries in the queue, including the head.
68
+ * @seealso {@link queued} for the queued count.
69
+ */
62
70
  get remaining() {
63
71
  return this.promises.length;
64
72
  }
73
+ /**
74
+ * The amount of queued entries.
75
+ * @seealso {@link remaining} for the count with the head.
76
+ */
65
77
  get queued() {
66
78
  return this.remaining === 0 ? 0 : this.remaining - 1;
67
79
  }
80
+ /**
81
+ * Waits for last promise and queues a new one
82
+ * @example
83
+ * ```typescript
84
+ * const queue = new AsyncQueue();
85
+ * async function request(url, options) {
86
+ * await queue.wait({ signal: options.signal });
87
+ * try {
88
+ * const result = await fetch(url, options);
89
+ * // Do some operations with 'result'
90
+ * } finally {
91
+ * // Remove first entry from the queue and resolve for the next entry
92
+ * queue.shift();
93
+ * }
94
+ * }
95
+ *
96
+ * request(someUrl1, someOptions1); // Will call fetch() immediately
97
+ * request(someUrl2, someOptions2); // Will call fetch() after the first finished
98
+ * request(someUrl3, someOptions3); // Will call fetch() after the second finished
99
+ * ```
100
+ */
68
101
  wait(options) {
69
102
  const entry = new AsyncQueueEntry(this);
70
103
  if (this.promises.length === 0) {
@@ -76,6 +109,9 @@ var AsyncQueue = class {
76
109
  entry.setSignal(options.signal);
77
110
  return entry.promise;
78
111
  }
112
+ /**
113
+ * Unlocks the head lock and transfers the next lock (if any) to the head.
114
+ */
79
115
  shift() {
80
116
  if (this.promises.length === 0)
81
117
  return;
@@ -86,6 +122,10 @@ var AsyncQueue = class {
86
122
  this.promises.shift();
87
123
  this.promises[0].use();
88
124
  }
125
+ /**
126
+ * Aborts all the pending promises.
127
+ * @note To avoid race conditions, this does **not** unlock the head lock.
128
+ */
89
129
  abortAll() {
90
130
  if (this.queued === 0)
91
131
  return;
@@ -95,7 +135,8 @@ var AsyncQueue = class {
95
135
  this.promises.length = 1;
96
136
  }
97
137
  };
98
- __name(AsyncQueue, "AsyncQueue");
138
+ __name(_AsyncQueue, "AsyncQueue");
139
+ var AsyncQueue = _AsyncQueue;
99
140
 
100
141
  export { AsyncQueue };
101
142
  //# sourceMappingURL=out.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/AsyncQueueEntry.ts","../src/lib/AsyncQueue.ts"],"names":[],"mappings":";;;;;;;;;AAKO,IAAM,kBAAN,MAAsB;AAAA,EAQrB,YAAY,OAAmB;AAPtC,wBAAgB;AAChB,wBAAQ;AACR,wBAAQ;AACR,wBAAiB;AACjB,wBAAQ,UAAqC;AAC7C,wBAAQ,kBAAsC;AAG7C,SAAK,QAAQ;AACb,SAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IACf,CAAC;AAAA,EACF;AAAA,EAEO,UAAU,QAAqB;AACrC,QAAI,OAAO;AAAS,aAAO;AAE3B,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC3B,YAAM,QAAQ,KAAK,MAAM,YAAY,QAAQ,IAAI;AACjD,UAAI,UAAU;AAAI,aAAK,MAAM,YAAY,OAAO,OAAO,CAAC;AAExD,WAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,IAClD;AACA,SAAK,OAAO,iBAAiB,SAAS,KAAK,cAAc;AACzD,WAAO;AAAA,EACR;AAAA,EAEO,MAAM;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EAEO,QAAQ;AACd,SAAK,QAAQ;AACb,SAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACjD,WAAO;AAAA,EACR;AAAA,EAEQ,UAAU;AACjB,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,oBAAoB,SAAS,KAAK,cAAe;AAC7D,WAAK,SAAS;AACd,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AACD;AAjDa;;;ACAN,IAAM,aAAN,MAAiB;AAAA,EAAjB;AAoBN,wBAAQ,YAA8B,CAAC;AAAA;AAAA,EAfvC,IAAW,YAAoB;AAC9B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA,EAMA,IAAW,SAAiB;AAC3B,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY;AAAA,EACpD;AAAA,EA4BO,KAAK,SAA0D;AACrE,UAAM,QAAQ,IAAI,gBAAgB,IAAI;AAEtC,QAAI,KAAK,SAAS,WAAW,GAAG;AAC/B,WAAK,SAAS,KAAK,KAAK;AACxB,aAAO,QAAQ,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,KAAK,KAAK;AACxB,QAAI,SAAS;AAAQ,YAAM,UAAU,QAAQ,MAAM;AACnD,WAAO,MAAM;AAAA,EACd;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,SAAS,WAAW;AAAG;AAChC,QAAI,KAAK,SAAS,WAAW,GAAG;AAE/B,WAAK,SAAS,MAAM;AACpB;AAAA,IACD;AAIA,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,GAAG,IAAI;AAAA,EACtB;AAAA,EAMO,WAAiB;AAEvB,QAAI,KAAK,WAAW;AAAG;AAIvB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,EAAE,GAAG;AAC9C,WAAK,SAAS,GAAG,MAAM;AAAA,IACxB;AAEA,SAAK,SAAS,SAAS;AAAA,EACxB;AACD;AAzFa","sourcesContent":["import type { AsyncQueue } from './AsyncQueue';\n\n/**\n * @internal\n */\nexport class AsyncQueueEntry {\n\tpublic readonly promise: Promise<void>;\n\tprivate resolve!: () => void;\n\tprivate reject!: (error: Error) => void;\n\tprivate readonly queue: AsyncQueue;\n\tprivate signal: PolyFillAbortSignal | null = null;\n\tprivate signalListener: (() => void) | null = null;\n\n\tpublic constructor(queue: AsyncQueue) {\n\t\tthis.queue = queue;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tthis.resolve = resolve;\n\t\t\tthis.reject = reject;\n\t\t});\n\t}\n\n\tpublic setSignal(signal: AbortSignal) {\n\t\tif (signal.aborted) return this;\n\n\t\tthis.signal = signal as PolyFillAbortSignal;\n\t\tthis.signalListener = () => {\n\t\t\tconst index = this.queue['promises'].indexOf(this);\n\t\t\tif (index !== -1) this.queue['promises'].splice(index, 1);\n\n\t\t\tthis.reject(new Error('Request aborted manually'));\n\t\t};\n\t\tthis.signal.addEventListener('abort', this.signalListener);\n\t\treturn this;\n\t}\n\n\tpublic use() {\n\t\tthis.dispose();\n\t\tthis.resolve();\n\t\treturn this;\n\t}\n\n\tpublic abort() {\n\t\tthis.dispose();\n\t\tthis.reject(new Error('Request aborted manually'));\n\t\treturn this;\n\t}\n\n\tprivate dispose() {\n\t\tif (this.signal) {\n\t\t\tthis.signal.removeEventListener('abort', this.signalListener!);\n\t\t\tthis.signal = null;\n\t\t\tthis.signalListener = null;\n\t\t}\n\t}\n}\n\ninterface PolyFillAbortSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: 'abort', listener: () => void): void;\n\tremoveEventListener(type: 'abort', listener: () => void): void;\n}\n","import { AsyncQueueEntry } from './AsyncQueueEntry';\n\n/**\n * The AsyncQueue class used to sequentialize burst requests\n */\nexport class AsyncQueue {\n\t/**\n\t * The amount of entries in the queue, including the head.\n\t * @seealso {@link queued} for the queued count.\n\t */\n\tpublic get remaining(): number {\n\t\treturn this.promises.length;\n\t}\n\n\t/**\n\t * The amount of queued entries.\n\t * @seealso {@link remaining} for the count with the head.\n\t */\n\tpublic get queued(): number {\n\t\treturn this.remaining === 0 ? 0 : this.remaining - 1;\n\t}\n\n\t/**\n\t * The promises array\n\t */\n\tprivate promises: AsyncQueueEntry[] = [];\n\n\t/**\n\t * Waits for last promise and queues a new one\n\t * @example\n\t * ```typescript\n\t * const queue = new AsyncQueue();\n\t * async function request(url, options) {\n\t * await queue.wait({ signal: options.signal });\n\t * try {\n\t * const result = await fetch(url, options);\n\t * // Do some operations with 'result'\n\t * } finally {\n\t * // Remove first entry from the queue and resolve for the next entry\n\t * queue.shift();\n\t * }\n\t * }\n\t *\n\t * request(someUrl1, someOptions1); // Will call fetch() immediately\n\t * request(someUrl2, someOptions2); // Will call fetch() after the first finished\n\t * request(someUrl3, someOptions3); // Will call fetch() after the second finished\n\t * ```\n\t */\n\tpublic wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void> {\n\t\tconst entry = new AsyncQueueEntry(this);\n\n\t\tif (this.promises.length === 0) {\n\t\t\tthis.promises.push(entry);\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tthis.promises.push(entry);\n\t\tif (options?.signal) entry.setSignal(options.signal);\n\t\treturn entry.promise;\n\t}\n\n\t/**\n\t * Unlocks the head lock and transfers the next lock (if any) to the head.\n\t */\n\tpublic shift(): void {\n\t\tif (this.promises.length === 0) return;\n\t\tif (this.promises.length === 1) {\n\t\t\t// Remove the head entry.\n\t\t\tthis.promises.shift();\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the head entry, making the 2nd entry the new one.\n\t\t// Then use the head entry, which will unlock the promise.\n\t\tthis.promises.shift();\n\t\tthis.promises[0].use();\n\t}\n\n\t/**\n\t * Aborts all the pending promises.\n\t * @note To avoid race conditions, this does **not** unlock the head lock.\n\t */\n\tpublic abortAll(): void {\n\t\t// If there are no queued entries, skip early.\n\t\tif (this.queued === 0) return;\n\n\t\t// Abort all the entries except the head, that is why the loop starts at\n\t\t// 1 and not at 0.\n\t\tfor (let i = 1; i < this.promises.length; ++i) {\n\t\t\tthis.promises[i].abort();\n\t\t}\n\n\t\tthis.promises.length = 1;\n\t}\n}\n\nexport interface AsyncQueueWaitOptions {\n\tsignal?: AbortSignal | undefined | null;\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/AsyncQueueEntry.ts","../src/lib/AsyncQueue.ts"],"names":[],"mappings":";;;;;;;;;AAKO,IAAM,mBAAN,MAAM,iBAAgB;AAAA,EAQrB,YAAY,OAAmB;AAPtC,wBAAgB;AAChB,wBAAQ;AACR,wBAAQ;AACR,wBAAiB;AACjB,wBAAQ,UAAqC;AAC7C,wBAAQ,kBAAsC;AAG7C,SAAK,QAAQ;AACb,SAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IACf,CAAC;AAAA,EACF;AAAA,EAEO,UAAU,QAAqB;AACrC,QAAI,OAAO;AAAS,aAAO;AAE3B,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC3B,YAAM,QAAQ,KAAK,MAAM,UAAU,EAAE,QAAQ,IAAI;AACjD,UAAI,UAAU;AAAI,aAAK,MAAM,UAAU,EAAE,OAAO,OAAO,CAAC;AAExD,WAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,IAClD;AACA,SAAK,OAAO,iBAAiB,SAAS,KAAK,cAAc;AACzD,WAAO;AAAA,EACR;AAAA,EAEO,MAAM;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EAEO,QAAQ;AACd,SAAK,QAAQ;AACb,SAAK,OAAO,IAAI,MAAM,0BAA0B,CAAC;AACjD,WAAO;AAAA,EACR;AAAA,EAEQ,UAAU;AACjB,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,oBAAoB,SAAS,KAAK,cAAe;AAC7D,WAAK,SAAS;AACd,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AACD;AAjD6B;AAAtB,IAAM,kBAAN;;;ACAA,IAAM,cAAN,MAAM,YAAW;AAAA,EAAjB;AAoBN;AAAA;AAAA;AAAA,wBAAQ,YAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAfvC,IAAW,YAAoB;AAC9B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAW,SAAiB;AAC3B,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BO,KAAK,SAA0D;AACrE,UAAM,QAAQ,IAAI,gBAAgB,IAAI;AAEtC,QAAI,KAAK,SAAS,WAAW,GAAG;AAC/B,WAAK,SAAS,KAAK,KAAK;AACxB,aAAO,QAAQ,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,KAAK,KAAK;AACxB,QAAI,SAAS;AAAQ,YAAM,UAAU,QAAQ,MAAM;AACnD,WAAO,MAAM;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,SAAS,WAAW;AAAG;AAChC,QAAI,KAAK,SAAS,WAAW,GAAG;AAE/B,WAAK,SAAS,MAAM;AACpB;AAAA,IACD;AAIA,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,CAAC,EAAE,IAAI;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,WAAiB;AAEvB,QAAI,KAAK,WAAW;AAAG;AAIvB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,EAAE,GAAG;AAC9C,WAAK,SAAS,CAAC,EAAE,MAAM;AAAA,IACxB;AAEA,SAAK,SAAS,SAAS;AAAA,EACxB;AACD;AAzFwB;AAAjB,IAAM,aAAN","sourcesContent":["import type { AsyncQueue } from './AsyncQueue';\n\n/**\n * @internal\n */\nexport class AsyncQueueEntry {\n\tpublic readonly promise: Promise<void>;\n\tprivate resolve!: () => void;\n\tprivate reject!: (error: Error) => void;\n\tprivate readonly queue: AsyncQueue;\n\tprivate signal: PolyFillAbortSignal | null = null;\n\tprivate signalListener: (() => void) | null = null;\n\n\tpublic constructor(queue: AsyncQueue) {\n\t\tthis.queue = queue;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tthis.resolve = resolve;\n\t\t\tthis.reject = reject;\n\t\t});\n\t}\n\n\tpublic setSignal(signal: AbortSignal) {\n\t\tif (signal.aborted) return this;\n\n\t\tthis.signal = signal as PolyFillAbortSignal;\n\t\tthis.signalListener = () => {\n\t\t\tconst index = this.queue['promises'].indexOf(this);\n\t\t\tif (index !== -1) this.queue['promises'].splice(index, 1);\n\n\t\t\tthis.reject(new Error('Request aborted manually'));\n\t\t};\n\t\tthis.signal.addEventListener('abort', this.signalListener);\n\t\treturn this;\n\t}\n\n\tpublic use() {\n\t\tthis.dispose();\n\t\tthis.resolve();\n\t\treturn this;\n\t}\n\n\tpublic abort() {\n\t\tthis.dispose();\n\t\tthis.reject(new Error('Request aborted manually'));\n\t\treturn this;\n\t}\n\n\tprivate dispose() {\n\t\tif (this.signal) {\n\t\t\tthis.signal.removeEventListener('abort', this.signalListener!);\n\t\t\tthis.signal = null;\n\t\t\tthis.signalListener = null;\n\t\t}\n\t}\n}\n\ninterface PolyFillAbortSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: 'abort', listener: () => void): void;\n\tremoveEventListener(type: 'abort', listener: () => void): void;\n}\n","import { AsyncQueueEntry } from './AsyncQueueEntry';\n\n/**\n * The AsyncQueue class used to sequentialize burst requests\n */\nexport class AsyncQueue {\n\t/**\n\t * The amount of entries in the queue, including the head.\n\t * @seealso {@link queued} for the queued count.\n\t */\n\tpublic get remaining(): number {\n\t\treturn this.promises.length;\n\t}\n\n\t/**\n\t * The amount of queued entries.\n\t * @seealso {@link remaining} for the count with the head.\n\t */\n\tpublic get queued(): number {\n\t\treturn this.remaining === 0 ? 0 : this.remaining - 1;\n\t}\n\n\t/**\n\t * The promises array\n\t */\n\tprivate promises: AsyncQueueEntry[] = [];\n\n\t/**\n\t * Waits for last promise and queues a new one\n\t * @example\n\t * ```typescript\n\t * const queue = new AsyncQueue();\n\t * async function request(url, options) {\n\t * await queue.wait({ signal: options.signal });\n\t * try {\n\t * const result = await fetch(url, options);\n\t * // Do some operations with 'result'\n\t * } finally {\n\t * // Remove first entry from the queue and resolve for the next entry\n\t * queue.shift();\n\t * }\n\t * }\n\t *\n\t * request(someUrl1, someOptions1); // Will call fetch() immediately\n\t * request(someUrl2, someOptions2); // Will call fetch() after the first finished\n\t * request(someUrl3, someOptions3); // Will call fetch() after the second finished\n\t * ```\n\t */\n\tpublic wait(options?: Readonly<AsyncQueueWaitOptions>): Promise<void> {\n\t\tconst entry = new AsyncQueueEntry(this);\n\n\t\tif (this.promises.length === 0) {\n\t\t\tthis.promises.push(entry);\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tthis.promises.push(entry);\n\t\tif (options?.signal) entry.setSignal(options.signal);\n\t\treturn entry.promise;\n\t}\n\n\t/**\n\t * Unlocks the head lock and transfers the next lock (if any) to the head.\n\t */\n\tpublic shift(): void {\n\t\tif (this.promises.length === 0) return;\n\t\tif (this.promises.length === 1) {\n\t\t\t// Remove the head entry.\n\t\t\tthis.promises.shift();\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the head entry, making the 2nd entry the new one.\n\t\t// Then use the head entry, which will unlock the promise.\n\t\tthis.promises.shift();\n\t\tthis.promises[0].use();\n\t}\n\n\t/**\n\t * Aborts all the pending promises.\n\t * @note To avoid race conditions, this does **not** unlock the head lock.\n\t */\n\tpublic abortAll(): void {\n\t\t// If there are no queued entries, skip early.\n\t\tif (this.queued === 0) return;\n\n\t\t// Abort all the entries except the head, that is why the loop starts at\n\t\t// 1 and not at 0.\n\t\tfor (let i = 1; i < this.promises.length; ++i) {\n\t\t\tthis.promises[i].abort();\n\t\t}\n\n\t\tthis.promises.length = 1;\n\t}\n}\n\nexport interface AsyncQueueWaitOptions {\n\tsignal?: AbortSignal | undefined | null;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sapphire/async-queue",
3
- "version": "1.5.1-next.3bbaad5.0",
3
+ "version": "1.5.1-next.3bc31a55.0",
4
4
  "description": "Sequential asynchronous lock-based queue for promises",
5
5
  "author": "@sapphire",
6
6
  "license": "MIT",
@@ -10,9 +10,14 @@
10
10
  "unpkg": "dist/index.global.js",
11
11
  "types": "dist/index.d.ts",
12
12
  "exports": {
13
- "import": "./dist/index.mjs",
14
- "require": "./dist/index.js",
15
- "types": "./dist/index.d.ts"
13
+ "import": {
14
+ "types": "./dist/index.d.mts",
15
+ "default": "./dist/index.mjs"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ }
16
21
  },
17
22
  "sideEffects": false,
18
23
  "homepage": "https://github.com/sapphiredev/utilities/tree/main/packages/async-queue",
@@ -56,12 +61,12 @@
56
61
  "access": "public"
57
62
  },
58
63
  "devDependencies": {
59
- "@favware/cliff-jumper": "^1.10.0",
60
- "@vitest/coverage-c8": "^0.26.3",
61
- "tsup": "^6.5.0",
62
- "typedoc": "^0.23.24",
63
- "typedoc-json-parser": "^7.1.0",
64
- "typescript": "^4.9.4",
65
- "vitest": "^0.26.3"
64
+ "@favware/cliff-jumper": "^2.2.0",
65
+ "@vitest/coverage-v8": "^0.34.6",
66
+ "tsup": "^7.2.0",
67
+ "typedoc": "^0.25.3",
68
+ "typedoc-json-parser": "^9.0.1",
69
+ "typescript": "^5.2.2",
70
+ "vitest": "^0.34.6"
66
71
  }
67
72
  }