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