1 | /**
|
2 | * Utility helpers to work with promises.
|
3 | *
|
4 | * @module promise
|
5 | */
|
6 |
|
7 | import * as time from './time.js'
|
8 |
|
9 | /**
|
10 | * @template T
|
11 | * @callback PromiseResolve
|
12 | * @param {T|PromiseLike<T>} [result]
|
13 | */
|
14 |
|
15 | /**
|
16 | * @template T
|
17 | * @param {function(PromiseResolve<T>,function(Error):void):any} f
|
18 | * @return {Promise<T>}
|
19 | */
|
20 | export const create = f => /** @type {Promise<T>} */ (new Promise(f))
|
21 |
|
22 | /**
|
23 | * @param {function(function():void,function(Error):void):void} f
|
24 | * @return {Promise<void>}
|
25 | */
|
26 | export const createEmpty = f => new Promise(f)
|
27 |
|
28 | /**
|
29 | * `Promise.all` wait for all promises in the array to resolve and return the result
|
30 | * @template {unknown[] | []} PS
|
31 | *
|
32 | * @param {PS} ps
|
33 | * @return {Promise<{ -readonly [P in keyof PS]: Awaited<PS[P]> }>}
|
34 | */
|
35 | export const all = Promise.all.bind(Promise)
|
36 |
|
37 | /**
|
38 | * @param {Error} [reason]
|
39 | * @return {Promise<never>}
|
40 | */
|
41 | export const reject = reason => Promise.reject(reason)
|
42 |
|
43 | /**
|
44 | * @template T
|
45 | * @param {T|void} res
|
46 | * @return {Promise<T|void>}
|
47 | */
|
48 | export const resolve = res => Promise.resolve(res)
|
49 |
|
50 | /**
|
51 | * @template T
|
52 | * @param {T} res
|
53 | * @return {Promise<T>}
|
54 | */
|
55 | export const resolveWith = res => Promise.resolve(res)
|
56 |
|
57 | /**
|
58 | * @todo Next version, reorder parameters: check, [timeout, [intervalResolution]]
|
59 | *
|
60 | * @param {number} timeout
|
61 | * @param {function():boolean} check
|
62 | * @param {number} [intervalResolution]
|
63 | * @return {Promise<void>}
|
64 | */
|
65 | export const until = (timeout, check, intervalResolution = 10) => create((resolve, reject) => {
|
66 | const startTime = time.getUnixTime()
|
67 | const hasTimeout = timeout > 0
|
68 | const untilInterval = () => {
|
69 | if (check()) {
|
70 | clearInterval(intervalHandle)
|
71 | resolve()
|
72 | } else if (hasTimeout) {
|
73 | /* c8 ignore else */
|
74 | if (time.getUnixTime() - startTime > timeout) {
|
75 | clearInterval(intervalHandle)
|
76 | reject(new Error('Timeout'))
|
77 | }
|
78 | }
|
79 | }
|
80 | const intervalHandle = setInterval(untilInterval, intervalResolution)
|
81 | })
|
82 |
|
83 | /**
|
84 | * @param {number} timeout
|
85 | * @return {Promise<undefined>}
|
86 | */
|
87 | export const wait = timeout => create((resolve, reject) => setTimeout(resolve, timeout))
|
88 |
|
89 | /**
|
90 | * Checks if an object is a promise using ducktyping.
|
91 | *
|
92 | * Promises are often polyfilled, so it makes sense to add some additional guarantees if the user of this
|
93 | * library has some insane environment where global Promise objects are overwritten.
|
94 | *
|
95 | * @param {any} p
|
96 | * @return {boolean}
|
97 | */
|
98 | export const isPromise = p => p instanceof Promise || (p && p.then && p.catch && p.finally)
|