UNPKG

3.05 kBJavaScriptView Raw
1/**
2 * Utility helpers to work with promises.
3 *
4 * @module promise
5 */
6
7import * 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 */
20export 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 */
26export 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 */
35export const all = Promise.all.bind(Promise)
36
37/**
38 * @param {Error} [reason]
39 * @return {Promise<never>}
40 */
41export const reject = reason => Promise.reject(reason)
42
43/**
44 * @template T
45 * @param {T|void} res
46 * @return {Promise<T|void>}
47 */
48export const resolve = res => Promise.resolve(res)
49
50/**
51 * @template T
52 * @param {T} res
53 * @return {Promise<T>}
54 */
55export const resolveWith = res => Promise.resolve(res)
56
57/**
58 * @todo Next version, reorder parameters: check, [timeout, [intervalResolution]]
59 * @deprecated use untilAsync instead
60 *
61 * @param {number} timeout
62 * @param {function():boolean} check
63 * @param {number} [intervalResolution]
64 * @return {Promise<void>}
65 */
66export const until = (timeout, check, intervalResolution = 10) => create((resolve, reject) => {
67 const startTime = time.getUnixTime()
68 const hasTimeout = timeout > 0
69 const untilInterval = () => {
70 if (check()) {
71 clearInterval(intervalHandle)
72 resolve()
73 } else if (hasTimeout) {
74 /* c8 ignore else */
75 if (time.getUnixTime() - startTime > timeout) {
76 clearInterval(intervalHandle)
77 reject(new Error('Timeout'))
78 }
79 }
80 }
81 const intervalHandle = setInterval(untilInterval, intervalResolution)
82})
83
84/**
85 * @param {()=>Promise<boolean>|boolean} check
86 * @param {number} timeout
87 * @param {number} intervalResolution
88 * @return {Promise<void>}
89 */
90export const untilAsync = async (check, timeout = 0, intervalResolution = 10) => {
91 const startTime = time.getUnixTime()
92 const noTimeout = timeout <= 0
93 // eslint-disable-next-line no-unmodified-loop-condition
94 while (noTimeout || time.getUnixTime() - startTime <= timeout) {
95 if (await check()) return
96 await wait(intervalResolution)
97 }
98 throw new Error('Timeout')
99}
100
101/**
102 * @param {number} timeout
103 * @return {Promise<undefined>}
104 */
105export const wait = timeout => create((resolve, _reject) => setTimeout(resolve, timeout))
106
107/**
108 * Checks if an object is a promise using ducktyping.
109 *
110 * Promises are often polyfilled, so it makes sense to add some additional guarantees if the user of this
111 * library has some insane environment where global Promise objects are overwritten.
112 *
113 * @param {any} p
114 * @return {boolean}
115 */
116export const isPromise = p => p instanceof Promise || (p && p.then && p.catch && p.finally)