@ndriadev/futurable 2.0.0 → 2.0.2

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.
Files changed (2) hide show
  1. package/package.json +91 -89
  2. package/src/index.ts +563 -0
package/package.json CHANGED
@@ -1,90 +1,92 @@
1
1
  {
2
- "name": "@ndriadev/futurable",
3
- "description": "Extension Javascript's Promise API with more functionalities",
4
- "private": false,
5
- "version": "2.0.0",
6
- "type": "module",
7
- "files": [
8
- "dist/",
9
- "scripts"
10
- ],
11
- "exports": {
12
- ".": {
13
- "types": "./dist/index.d.ts",
14
- "import": "./dist/futurable.mjs.js",
15
- "require": "./dist/futurable.cjs.js",
16
- "node": {
17
- "types": "./dist/index.d.ts",
18
- "require": "./dist/futurable.cjs.js",
19
- "import": "./dist/futurable.mjs.js"
20
- }
21
- }
22
- },
23
- "main": "./src/index.ts",
24
- "types": "./src/index.d.ts",
25
- "//scripts": {
26
- "preinstall": "node ./scripts/preinstall.js --foreground-script",
27
- "postinstall": "echo 'postinstall executed'"
28
- },
29
- "scripts": {
30
- "build": "tsc && vite build",
31
- "test": "NODE_OPTIONS=--experimental-vm-modules jest",
32
- "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
33
- "lint:fix": "eslint --fix 'src/**/*.{jsx,ts,tsx}'",
34
- "prepare": "pnpm run build",
35
- "prepublishOnly": "pnpm test && pnpm run lint"
36
- },
37
- "devDependencies": {
38
- "@babel/preset-typescript": "^7.21.5",
39
- "@jest/globals": "^29.5.0",
40
- "@types/node": "^20.1.1",
41
- "@typescript-eslint/eslint-plugin": "^5.59.5",
42
- "@typescript-eslint/parser": "^5.59.5",
43
- "eslint": "^8.40.0",
44
- "eslint-config-prettier": "^8.8.0",
45
- "eslint-plugin-import": "^2.27.5",
46
- "eslint-plugin-prettier": "^4.2.1",
47
- "jest": "^29.5.0",
48
- "prettier": "^2.8.8",
49
- "ts-jest": "^29.1.0",
50
- "ts-node": "^10.9.1",
51
- "typescript": "^5.0.2",
52
- "vite": "^4.3.2",
53
- "vite-plugin-dts": "^2.3.0",
54
- "vite-plugin-linter": "^2.0.2",
55
- "vite-tsconfig-paths": "^4.2.0"
56
- },
57
- "keywords": [
58
- "promise",
59
- "promises",
60
- "promises-a",
61
- "promises-aplus",
62
- "async",
63
- "await",
64
- "deferred",
65
- "deferreds",
66
- "future",
67
- "cancel",
68
- "abort",
69
- "delay",
70
- "sleep",
71
- "abortable",
72
- "cancelable",
73
- "futurable"
74
- ],
75
- "repository": {
76
- "type": "git",
77
- "url": "git+https://github.com/nDriaDev/futurable"
78
- },
79
- "author": {
80
- "name": "Andrea Cosentino",
81
- "email": "andreacosentino.work@gmail.com",
82
- "url": "https://github.com/nDriaDev/"
83
- },
84
- "readmeFilename": "README.md",
85
- "bugs": {
86
- "url": "https://github.com/nDriaDev/futurable/issues"
87
- },
88
- "homepage": "https://github.com/nDriaDev/futurable",
89
- "license": "MIT"
90
- }
2
+ "name": "@ndriadev/futurable",
3
+ "description": "Extension Javascript's Promise API with more functionalities",
4
+ "private": false,
5
+ "version": "2.0.2",
6
+ "type": "module",
7
+ "files": [
8
+ "dist/",
9
+ "scripts"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/futurable.mjs.js",
15
+ "require": "./dist/futurable.cjs.js",
16
+ "node": {
17
+ "types": "./dist/index.d.ts",
18
+ "require": "./dist/futurable.cjs.js",
19
+ "import": "./dist/futurable.mjs.js"
20
+ }
21
+ }
22
+ },
23
+ "main": "./src/index.ts",
24
+ "types": "./src/index.d.ts",
25
+ "//scripts": {
26
+ "preinstall": "node ./scripts/preinstall.js --foreground-script",
27
+ "postinstall": "echo 'postinstall executed'"
28
+ },
29
+ "devDependencies": {
30
+ "@babel/preset-typescript": "^7.21.5",
31
+ "@jest/globals": "^29.5.0",
32
+ "@types/node": "^20.1.1",
33
+ "@typescript-eslint/eslint-plugin": "^5.59.5",
34
+ "@typescript-eslint/parser": "^5.59.5",
35
+ "eslint": "^8.40.0",
36
+ "eslint-config-prettier": "^8.8.0",
37
+ "eslint-plugin-import": "^2.27.5",
38
+ "eslint-plugin-prettier": "^4.2.1",
39
+ "jest": "^29.5.0",
40
+ "prettier": "^2.8.8",
41
+ "ts-jest": "^29.1.0",
42
+ "ts-node": "^10.9.1",
43
+ "typescript": "^5.0.2",
44
+ "vite": "^4.3.2",
45
+ "vite-plugin-dts": "^2.3.0",
46
+ "vite-plugin-linter": "^2.0.2",
47
+ "vite-tsconfig-paths": "^4.2.0"
48
+ },
49
+ "keywords": [
50
+ "promise",
51
+ "promises",
52
+ "promises-a",
53
+ "promises-aplus",
54
+ "async",
55
+ "await",
56
+ "deferred",
57
+ "deferreds",
58
+ "future",
59
+ "cancel",
60
+ "abort",
61
+ "delay",
62
+ "sleep",
63
+ "abortable",
64
+ "cancelable",
65
+ "futurable"
66
+ ],
67
+ "repository": {
68
+ "type": "git",
69
+ "url": "git+https://github.com/nDriaDev/futurable"
70
+ },
71
+ "author": {
72
+ "name": "Andrea Cosentino",
73
+ "email": "andreacosentino.work@gmail.com",
74
+ "url": "https://github.com/nDriaDev/"
75
+ },
76
+ "readmeFilename": "README.md",
77
+ "bugs": {
78
+ "url": "https://github.com/nDriaDev/futurable/issues"
79
+ },
80
+ "homepage": "https://github.com/nDriaDev/futurable",
81
+ "license": "MIT",
82
+ "scripts": {
83
+ "build": "tsc && vite build",
84
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
85
+ "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
86
+ "lint:fix": "eslint --fix 'src/**/*.{jsx,ts,tsx}'",
87
+ "release:patch": "pnpm version patch",
88
+ "release:minor": "pnpm version minor",
89
+ "release:major": "pnpm version major",
90
+ "postversion": "git push && git push origin --tags && pnpm publish --access public"
91
+ }
92
+ }
package/src/index.ts ADDED
@@ -0,0 +1,563 @@
1
+ export interface FuturableLike<T> {
2
+ /**
3
+ * Attaches callbacks for the resolution and/or rejection of the Futurable.
4
+ * @param onfulfilled The callback to execute when the Futurable is resolved.
5
+ * @param onrejected The callback to execute when the Futurable is rejected.
6
+ * @returns A Futurable for the completion of which ever callback is executed.
7
+ */
8
+ then<TResult1 = T, TResult2 = never>(
9
+ onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>) | undefined | null,
10
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null
11
+ ): FuturableLike<TResult1 | TResult2>;
12
+ }
13
+ export interface FuturableResolve<T> {
14
+ (value: T | FuturableLike<T> | PromiseLike<T>): void;
15
+ }
16
+
17
+ export interface FuturableReject {
18
+ (reason?: any): void;
19
+ }
20
+
21
+ export interface FuturableUtils<T> {
22
+ /**
23
+ * Internal futurable signal
24
+ */
25
+ signal: AbortSignal;
26
+ /**
27
+ * Cancel the futurable if it is to be executed or if it is still executing.
28
+ */
29
+ cancel: () => void;
30
+ /**
31
+ * Executes the callback passed as a parameter when the futurable is cancelled.
32
+ * @param cb: callback
33
+ */
34
+ onCancel: (cb: () => void) => void;
35
+ /**
36
+ * Waits for timer, then executes callback with the futurable value and returns the result obtained from the invocation.
37
+ * @param cb: callback executed after timer
38
+ * @param timer: timer to wait (in milliseconds)
39
+ */
40
+ delay: <TResult=T, TResult2=never>(cb: () => TResult, timer: number) => FuturableLike<TResult | TResult2>;
41
+ /**
42
+ * Waits for timer parameter (in milliseconds) before returning the value.
43
+ * @param timer: timer to wait (in milliseconds)
44
+ */
45
+ sleep: (timer: number) => FuturableLike<void>;
46
+ /**
47
+ * Extension of the fetch API with cancellation support. Url parameter can be a string or a function with receive value from futurable chaining as paremeter.
48
+ * @param url: url to fetch
49
+ * @param opts: fetch options
50
+ */
51
+ fetch: (url: string, opts?: RequestInit) => Futurable<Response>;
52
+ /**
53
+ * Takes a promise and transforms it into a futurizable. Promise can be also a function that receives value from futurable chaining as parameter.
54
+ * @param promise: Promise to futurize
55
+ */
56
+ futurizable: <TResult=any>(promise: Promise<TResult>) => Futurable<TResult>;
57
+ }
58
+
59
+ export type FuturableExecutor<T> = (
60
+ resolve: FuturableResolve<T>,
61
+ reject: FuturableReject,
62
+ /**
63
+ * Object containing implemented functionalities.
64
+ */
65
+ utils: FuturableUtils<T>
66
+ ) => void;
67
+
68
+ export type FuturableIterable<T = any> = Iterable<FuturableLike<T> | PromiseLike<T> | T>;
69
+
70
+ enum FUTURABLE_STATUS {
71
+ PENDING = "pending",
72
+ FULFILLED = "fulfilled",
73
+ REJECTED = "rejected"
74
+ }
75
+
76
+ export class Futurable<T> extends Promise<T> {
77
+ private controller;
78
+ private internalSignal;
79
+ private idsTimeout;
80
+
81
+ constructor(executor: FuturableExecutor<T>, signal?: AbortSignal) {
82
+ const controller: AbortController | null = signal ? null : new AbortController();
83
+ const sign = signal || controller!.signal;
84
+ const idsTimeout: ReturnType<typeof setTimeout>[] = [];
85
+
86
+ const abortTimeout = () => {
87
+ for (const timeout of idsTimeout) {
88
+ clearTimeout(timeout);
89
+ }
90
+ };
91
+
92
+ let abort: () => void;
93
+
94
+ const onCancel = (cb: () => void): void => {
95
+ abort = cb;
96
+ };
97
+
98
+ const utils: FuturableUtils<T> = {
99
+ signal: sign,
100
+ cancel: (): void => this.controller?.abort(),
101
+ onCancel,
102
+ delay: (cb, timer) => {
103
+ return new Futurable(res => {
104
+ idsTimeout.push(setTimeout(() => {
105
+ res(cb());
106
+ }, timer));
107
+ }, sign);
108
+ },
109
+ sleep: (timer) => {
110
+ return utils.delay(() => { }, timer);
111
+ },
112
+ fetch: (url: string, opts?: RequestInit): Futurable<Response> => {
113
+ return new Futurable<Response>((res, rej) => {
114
+ fetch(url, { ...(opts || {}), signal: sign })
115
+ .then(val => res(val))
116
+ .catch(err => {
117
+ if (err.name === "AbortError") {
118
+ return;
119
+ } else {
120
+ rej(err);
121
+ }
122
+ });
123
+ }, sign);
124
+ },
125
+ futurizable: (promise) => {
126
+ return new Futurable((res, rej) => {
127
+ promise
128
+ .then(res)
129
+ .catch(rej);
130
+ }, sign);
131
+ }
132
+ };
133
+
134
+ let status = FUTURABLE_STATUS.PENDING;
135
+
136
+ const p = new Promise<T>((resolve, reject) => {
137
+ if (!sign.aborted) {
138
+ const func: (() => void) = typeof sign.onabort === "function" ? sign.onabort as () => void : () => { };
139
+ sign.onabort = () => {
140
+ func();
141
+ abortTimeout();
142
+ if (status === FUTURABLE_STATUS.PENDING) {
143
+ abort && abort();
144
+ }
145
+ return;
146
+ };
147
+
148
+ const res: FuturableResolve<T> = (val) => {
149
+ status = FUTURABLE_STATUS.FULFILLED;
150
+ resolve(val as T | PromiseLike<T>);
151
+ };
152
+
153
+ const rej: FuturableReject = (reason) => {
154
+ status = FUTURABLE_STATUS.REJECTED;
155
+ reject(reason);
156
+ };
157
+
158
+ executor(res, rej, utils);
159
+ } else {
160
+ abortTimeout();
161
+ status === FUTURABLE_STATUS.PENDING && abort && abort();
162
+ return;
163
+ }
164
+ });
165
+ super((resolve, reject) => {
166
+ p.then(val => resolve(val)).catch(reject);
167
+ });
168
+ this.controller = controller;
169
+ this.internalSignal = sign;
170
+ this.idsTimeout = idsTimeout;
171
+ }
172
+
173
+ static get [Symbol.species]() {
174
+ return this;
175
+ }
176
+
177
+ get [Symbol.toStringTag]() {
178
+ return 'Futurable';
179
+ }
180
+
181
+ /**
182
+ * Return internal futurable signal
183
+ */
184
+ get signal() {
185
+ return this.internalSignal;
186
+ }
187
+
188
+ private clearTimeout() {
189
+ for (const timeout of this.idsTimeout) {
190
+ clearTimeout(timeout);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Attaches callbacks for the resolution and/or rejection of the Futurable.
196
+ */
197
+ then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null): Futurable<TResult1 | TResult2> {
198
+ let resolve: FuturableResolve<TResult1|TResult2>, reject: FuturableReject;
199
+ const p = new Futurable<TResult1|TResult2>((res, rej) => {
200
+ resolve = res;
201
+ reject = rej;
202
+ }, this.internalSignal);
203
+ p.controller = this.controller;
204
+ super.then(val => {
205
+ if (this.internalSignal?.aborted) {
206
+ this.clearTimeout();
207
+ return;
208
+ }
209
+ try {
210
+ if (onfulfilled) {
211
+ resolve(onfulfilled(val));
212
+ } else {
213
+ resolve(val as unknown as TResult1);
214
+ }
215
+ } catch (error) {
216
+ reject(error);
217
+ }
218
+ }, reason => {
219
+ if (this.internalSignal?.aborted) {
220
+ this.clearTimeout();
221
+ return;
222
+ }
223
+ try {
224
+ if (onrejected) {
225
+ resolve(onrejected(reason));
226
+ } else {
227
+ reject(reason);
228
+ }
229
+ } catch (error) {
230
+ reject(error);
231
+ }
232
+ });
233
+ return p;
234
+ }
235
+
236
+ /**
237
+ * Attaches a callback for only the rejection of the Futurable.
238
+ */
239
+ catch<TResult2 = never>(onRejected: ((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null): Futurable<T | TResult2> {
240
+ return this.then(null, onRejected);
241
+ }
242
+
243
+ /**
244
+ * Attaches a callback that is invoked when the Futurable is settled (fulfilled or rejected).
245
+ * The resolved value cannot be modified from the callback.
246
+ */
247
+ finally(onfinally: () => void | undefined | null): Futurable<T> {
248
+ return this.then(
249
+ (val) => {
250
+ onfinally();
251
+ return val;
252
+ },
253
+ (reason) => {
254
+ onfinally();
255
+ return reason;
256
+ }
257
+ );
258
+ }
259
+
260
+ /**
261
+ * Cancel the futurable if it is to be executed or if it is still executing.
262
+ */
263
+ cancel(): void {
264
+ !this.internalSignal?.aborted && this.controller?.abort();
265
+ }
266
+
267
+ /**
268
+ * Waits for timer, then executes callback with the futurable value and returns the result obtained from the invocation.
269
+ * @param cb: callback executed after timer with futurable chain value as parameter
270
+ * @param timer: timer to wait (in milliseconds)
271
+ */
272
+ delay<TResult1 = T, TResult2 = never>(cb: (val: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>, timer: number): Futurable<TResult1 | TResult2> {
273
+ let resolve: FuturableResolve<TResult1 | TResult2>, reject: FuturableReject;
274
+ const p = new Futurable<TResult1 | TResult2>((res, rej) => {
275
+ resolve = res;
276
+ reject = rej;
277
+ }, this.internalSignal);
278
+ p.controller = this.controller;
279
+ this.then(
280
+ val => {
281
+ this.idsTimeout.push(setTimeout(() => resolve(cb(val)), timer));
282
+ },
283
+ reason => {
284
+ reject(reason);
285
+ }
286
+ );
287
+ return p;
288
+ }
289
+
290
+ /**
291
+ * Waits for timer parameter (in milliseconds) before returning the value.
292
+ * @param timer: timer to wait (in milliseconds)
293
+ */
294
+ sleep(timer: number): Futurable<T> {
295
+ return this.delay(val => val, timer);
296
+ }
297
+
298
+ /**
299
+ * Extension of the fetch API with cancellation support. Url parameter can be a string or a function with receive value from futurable chaining as paremeter.
300
+ * @param url: url to fetch or function with futurable chaining value that returns url to fetch
301
+ * @param opts: fetch options or function with futurable chaining value that return fetch options
302
+ */
303
+ fetch(url: string | ((val?: T) => string), opts?: object | RequestInit | ((val?: T) => RequestInit)): Futurable<Response> {
304
+ let resolve: FuturableResolve<Response>, reject: FuturableReject;
305
+ const p = new Futurable<Response>((res, rej) => {
306
+ resolve = res;
307
+ reject = rej;
308
+ }, this.internalSignal);
309
+ p.controller = this.controller;
310
+ this.then(val => {
311
+ const urlFetch = typeof url === "function" ? url(val) : url,
312
+ optsFetch = { ...(typeof opts === "function" ? opts(val) : opts), signal: this.internalSignal };
313
+
314
+ fetch(urlFetch, optsFetch).then(val => resolve(val)).catch(err => {
315
+ if (err.name === "AbortError") {
316
+ return;
317
+ } else {
318
+ reject(err);
319
+ }
320
+ });
321
+ });
322
+ return p;
323
+ }
324
+
325
+ /**
326
+ * Executes the callback passed as a parameter when the futurable is cancelled.
327
+ * @param cb: callback
328
+ */
329
+ onCancel<TResult1 = void, TResult2 = never>(cb: () => void): Futurable<TResult1 | TResult2> {
330
+ let resolve: FuturableResolve<TResult1 | TResult2>, reject: FuturableReject;
331
+ const f = new Futurable<TResult1 | TResult2>((res, rej, utils) => {
332
+ utils.onCancel(cb);
333
+ resolve = res;
334
+ reject = rej;
335
+ }, this.internalSignal);
336
+ f.controller = this.controller;
337
+
338
+ this.then(
339
+ val => resolve(val as unknown as TResult1),
340
+ reason => reject(reason)
341
+ );
342
+ return f;
343
+ }
344
+
345
+ // promisify<TResult1 = T, TResult2 = never>(): Promise<TResult1 | TResult2> {
346
+ // return new Promise((res, rej) => {
347
+ // if (this.#signal.aborted) {
348
+ // this.#clearTimeout();
349
+ // return;
350
+ // } else {
351
+ // this.then(
352
+ // val => res(val),
353
+ // reason => rej(reason)
354
+ // );
355
+ // }
356
+ // });
357
+ // }
358
+
359
+ /**
360
+ * Takes a promise and transforms it into a futurizable. Promise can be also a function that receives value from futurable chaining as parameter.
361
+ * @param promise: Promise to futurize or function that return promise with futurable chaining value as parameter
362
+ */
363
+ futurizable<TResult1 = T, TResult2 = never>(promise: Promise<TResult1> | ((val?: T) => Promise<TResult1>)): Futurable<TResult1 | TResult2> {
364
+ let resolve: FuturableResolve<TResult1 | TResult2>, reject: FuturableReject;
365
+ const f = new Futurable<TResult1 | TResult2>((res, rej) => {
366
+ resolve = res;
367
+ reject = rej;
368
+ }, this.internalSignal);
369
+ f.controller = this.controller;
370
+ this.then(val => {
371
+ const p = typeof promise === "function" ? promise(val) : promise;
372
+ p
373
+ .then(resolve)
374
+ .catch(reject);
375
+ });
376
+ return f;
377
+ }
378
+
379
+ static resolve(): Futurable<void>;
380
+ static resolve<T=any>(value: T | PromiseLike<T> | FuturableLike<T>, signal?: AbortSignal): Futurable<T>;
381
+ static resolve<T=any>(value?: T | PromiseLike<T> | FuturableLike<T>, signal?: AbortSignal): Futurable<T|void> {
382
+ return value
383
+ ? new Futurable(res => res(value), signal)
384
+
385
+ : new Futurable<void>(res=> res(), signal);
386
+ }
387
+
388
+ static reject<T = never>(reason?: any, signal?: AbortSignal): Futurable<T> {
389
+ return new Futurable((res, rej) => rej(reason), signal);
390
+ }
391
+
392
+ /**
393
+ * OnCancel static method. It accepts a callback or a object with cb property and an optional signal.
394
+ */
395
+ static onCancel<T=void>({ cb, signal }: {cb: () => T, signal?: AbortSignal}): Futurable<T> {
396
+ return new Futurable((res, rej, utils) => {
397
+ utils.onCancel(() => res(cb()));
398
+ }, signal);
399
+ }
400
+
401
+ /**
402
+ * Delay static method. It accepts a object with timer and cb properties and an optional signal property.
403
+ */
404
+ static delay<T = any, TResult2 = never>({ cb, timer, signal }: { cb: () => any, timer: number, signal?: AbortSignal }): Futurable<T | TResult2> {
405
+ return new Futurable((res, rej, utils) => {
406
+ utils.delay(cb, timer).then(res, rej);
407
+ }, signal)
408
+ }
409
+
410
+ /**
411
+ * Sleep static method. It accepts a timer or a object with timer property and an optional signal.
412
+ */
413
+ static sleep({ timer, signal }: { timer: number, signal?: AbortSignal }): Futurable<void> {
414
+ return Futurable.delay<void>({
415
+ cb: () => { },
416
+ timer,
417
+ signal
418
+ });
419
+ }
420
+
421
+ /**
422
+ * Fetch static method.
423
+ */
424
+ static fetch(url: string, opts?: RequestInit): Futurable<Response> {
425
+ const signal = opts?.signal || undefined;
426
+ opts?.signal && delete opts.signal;
427
+ return new Futurable((res, rej, utils) => {
428
+ utils.fetch(url, opts).then(res);
429
+ }, signal)
430
+ }
431
+
432
+ /**
433
+ * Futurizable static method.
434
+ */
435
+ static futurizable<TResult1=any, TResult2=never>({ promise, signal }: { promise: Promise<TResult1>, signal?: AbortSignal }): Futurable<TResult1 | TResult2> {
436
+ return new Futurable((res, rej) => {
437
+ promise
438
+ .then(res)
439
+ .catch(rej);
440
+ }, signal);
441
+ }
442
+
443
+ private static handleValues<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<T>[] {
444
+ const array: Futurable<T>[] = [];
445
+
446
+ for (const i in values) {
447
+ if ((values[i] instanceof Futurable)) {
448
+ array.push(values[i] as Futurable<{ -readonly [P in keyof T]: T[P] }>);
449
+ }
450
+ else if ((values[i] instanceof Promise)) {
451
+ array.push(
452
+ new Futurable<{ - readonly [P in keyof T]: T[P] }>(
453
+ (res, rej) => {
454
+ (values[i] as Promise<{ -readonly [P in keyof T]: T[P] }>)
455
+ .then((val) => res(val))
456
+ .catch(rej);
457
+ },
458
+ signal
459
+ )
460
+ );
461
+ } else {
462
+ array.push(
463
+ new Futurable<{ - readonly [P in keyof T]: T[P] }>(
464
+ res => res(values[i] as { -readonly [P in keyof T]: T[P] | FuturableLike<T[P]> | PromiseLike<T[P]> }),
465
+ signal
466
+ )
467
+ );
468
+ }
469
+ }
470
+
471
+ return array;
472
+ }
473
+
474
+ /**
475
+ * Creates a Futurable with cancellation support that is resolved with an array of results when all of the provided Futurables resolve, or rejected when any Futurable is rejected.
476
+ */
477
+ static all<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<{ -readonly [P in keyof T]: Awaited<T[P]>; }> {
478
+ let resolve: FuturableResolve<{ -readonly [P in keyof T]: Awaited<T[P]> }>, reject: FuturableReject;
479
+ const f = new Futurable<{ -readonly [P in keyof T]: Awaited<T[P]> }>((res, rej, utils) => {
480
+ resolve = res;
481
+ reject = rej;
482
+ utils.onCancel(() => {
483
+ for (const futurable of array) {
484
+ futurable.cancel();
485
+ }
486
+ })
487
+ }, signal);
488
+ signal ||= f.internalSignal;
489
+ const array = Futurable.handleValues(values, signal);
490
+
491
+ super.all(array).then(val => resolve(val as { -readonly [P in keyof T]: Awaited<T[P]>; })).catch(reason => reject(reason));
492
+
493
+ return f;
494
+ }
495
+
496
+ /**
497
+ * Creates a Futurable with cancellation support that is resolved with an array of results when all of the provided Futurables resolve or reject.
498
+ */
499
+ static allSettled<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }> {
500
+ let resolve: FuturableResolve<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>>; }>;
501
+ const f = new Futurable<{ - readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }>((res, rej, utils) => {
502
+ resolve = res;
503
+ utils.onCancel(() => {
504
+ for (const futurable of array) {
505
+ futurable.cancel();
506
+ }
507
+ })
508
+ }, signal);
509
+ signal ||= f.internalSignal;
510
+ const array = Futurable.handleValues(values, signal);
511
+
512
+ super.allSettled(array).then(val => resolve(val as { -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>>; }));
513
+
514
+ return f;
515
+ }
516
+
517
+ /**
518
+ * Creates a Futurable with cancellation support that is resolved or rejected when any of the provided Futurables are resolved or rejected.
519
+ */
520
+ static race<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<Awaited<T[number]>> {
521
+ let resolve: FuturableResolve<Awaited<T[number]>>, reject: FuturableReject;
522
+ const f = new Futurable<Awaited<T[number]>>((res, rej, utils) => {
523
+ resolve = res;
524
+ reject = rej;
525
+ utils.onCancel(() => {
526
+ for (const futurable of array) {
527
+ futurable.cancel();
528
+ }
529
+ })
530
+ }, signal);
531
+ signal ||= f.internalSignal;
532
+ const array = Futurable.handleValues(values, signal);
533
+
534
+ super.race(array).then(val => resolve(val)).catch(reason => reject(reason));
535
+
536
+ return f;
537
+ }
538
+
539
+ /**
540
+ * The any function returns a futurable with cancellation support that is fulfilled by the first given futurable to be fulfilled,
541
+ * or rejected with an AggregateError containing an array of rejection reasons if all of the
542
+ * given futurables are rejected. It resolves all elements of the passed iterable to futurables as
543
+ * it runs this algorithm.
544
+ */
545
+ static any<T extends readonly unknown[] | []>(value: T, signal?: AbortSignal): Futurable<Awaited<T[number]>> {
546
+ let resolve: FuturableResolve<Awaited<T[number]>>, reject: FuturableReject;
547
+ const f = new Futurable<Awaited<T[number]>>((res, rej, utils) => {
548
+ resolve = res;
549
+ reject = rej;
550
+ utils.onCancel(() => {
551
+ for (const futurable of array) {
552
+ futurable.cancel();
553
+ }
554
+ })
555
+ }, signal);
556
+ signal ||= f.internalSignal;
557
+ const array = Futurable.handleValues(value, signal);
558
+
559
+ super.any(array).then(val => resolve(val)).catch(reason => reject(reason));
560
+
561
+ return f;
562
+ }
563
+ }