@creejs/commons-lang 1.0.1
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 +2 -0
- package/index.js +2 -0
- package/lib/exec-utils.js +58 -0
- package/lib/index.js +26 -0
- package/lib/lang-utils.js +110 -0
- package/lib/promise-utils.js +317 -0
- package/lib/string-utils.js +428 -0
- package/lib/type-assert.js +253 -0
- package/lib/type-utils.js +290 -0
- package/package.json +31 -0
- package/types/exec-utils.d.ts +14 -0
- package/types/index.d.ts +7 -0
- package/types/lang-utils.d.ts +58 -0
- package/types/lang.d.ts +5 -0
- package/types/promise-utils.d.ts +94 -0
- package/types/string-utils.d.ts +152 -0
- package/types/type-assert.d.ts +91 -0
- package/types/type-asset.d.ts +84 -0
- package/types/type-utils.d.ts +158 -0
package/README.md
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/**
|
|
3
|
+
* @module ExecUtils
|
|
4
|
+
* @description Utils about how to execute task functions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
const { assertFunction, assertArray } = require('./type-assert')
|
|
9
|
+
const { isPromise } = require('./type-utils')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Executes a task silently, suppressing any errors or rejections.
|
|
13
|
+
* @param {Function} task - The function to execute.
|
|
14
|
+
* @returns {Promise<*>|*} The return value of the task, or a Promise if the task is asynchronous.
|
|
15
|
+
*/
|
|
16
|
+
function quiet (task) {
|
|
17
|
+
assertFunction(task)
|
|
18
|
+
try {
|
|
19
|
+
const rtnVal = task()
|
|
20
|
+
if (isPromise(rtnVal)) {
|
|
21
|
+
return rtnVal.catch(() => {
|
|
22
|
+
// do nothing
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
return rtnVal
|
|
26
|
+
} catch (e) {
|
|
27
|
+
// do nothing
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Executes a task quietly, capturing any errors and passing them to the errorKeeper.
|
|
33
|
+
* 1. Handles both synchronous and asynchronous (Promise) tasks.
|
|
34
|
+
* @param {Function} task - The function to execute.
|
|
35
|
+
* @param {Error[]} errorKeeper - The array to store any caught errors.
|
|
36
|
+
* @returns {Promise<*>|*} The return value of the task, or a Promise if the task is asynchronous.
|
|
37
|
+
*/
|
|
38
|
+
function quietKeepError (task, errorKeeper) {
|
|
39
|
+
assertFunction(task)
|
|
40
|
+
assertArray(errorKeeper)
|
|
41
|
+
try {
|
|
42
|
+
const rtnVal = task()
|
|
43
|
+
if (isPromise(rtnVal)) {
|
|
44
|
+
// @ts-ignore
|
|
45
|
+
return rtnVal.catch(e => errorKeeper.push(e))
|
|
46
|
+
}
|
|
47
|
+
return rtnVal
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
errorKeeper.push(e)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const ExecUtils = {
|
|
54
|
+
quiet,
|
|
55
|
+
quietKeepError
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = ExecUtils
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module Lang
|
|
5
|
+
* @description Core language utilities for type checking, string manipulation, and common operations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const LangUtils = require('./lang-utils')
|
|
9
|
+
const StringUtils = require('./string-utils')
|
|
10
|
+
const TypeUtils = require('./type-utils')
|
|
11
|
+
const TypeAssert = require('./type-assert')
|
|
12
|
+
const ExecUtils = require('./exec-utils')
|
|
13
|
+
const PromiseUtils = require('./promise-utils')
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
LangUtils,
|
|
17
|
+
StringUtils,
|
|
18
|
+
TypeUtils,
|
|
19
|
+
TypeAssert,
|
|
20
|
+
ExecUtils,
|
|
21
|
+
PromiseUtils,
|
|
22
|
+
Lang: LangUtils,
|
|
23
|
+
String: StringUtils,
|
|
24
|
+
Type: TypeUtils,
|
|
25
|
+
Exec: ExecUtils
|
|
26
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module LangUtils
|
|
5
|
+
* @description Language utility functions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Gets the constructor name of a value.
|
|
10
|
+
* @param {*} value - The value to check.
|
|
11
|
+
* @returns {string|undefined} The constructor name, or undefined if value has no constructor.
|
|
12
|
+
*/
|
|
13
|
+
function constructorName (value) {
|
|
14
|
+
return value?.constructor?.name
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Assigns default values from source objects to target object for undefined properties.
|
|
19
|
+
* @param {Object<string, any>} target - The target object to assign defaults to
|
|
20
|
+
* @param {...Object<string, any>} sources - Source objects containing default values
|
|
21
|
+
* @returns {Object<string, any>} The modified target object with defaults applied
|
|
22
|
+
* @throws {TypeError} If target is null or undefined
|
|
23
|
+
*/
|
|
24
|
+
function defaults (target, ...sources) {
|
|
25
|
+
if (target == null) {
|
|
26
|
+
throw new TypeError('"target" must not be null or undefined')
|
|
27
|
+
}
|
|
28
|
+
for (const source of sources) {
|
|
29
|
+
if (source == null) {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
for (const key in source) {
|
|
33
|
+
if (target[key] === undefined) {
|
|
34
|
+
target[key] = source[key]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return target
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extends a target object with properties from one or more source objects.
|
|
43
|
+
* @param {Object<string, any>} target - The target object to extend (must not be null/undefined)
|
|
44
|
+
* @param {...Object<string, any>} sources - One or more source objects to copy properties from
|
|
45
|
+
* @returns {Object<string, any>} The modified target object
|
|
46
|
+
* @throws {TypeError} If target is null or undefined
|
|
47
|
+
*/
|
|
48
|
+
function extend (target, ...sources) {
|
|
49
|
+
if (target == null) {
|
|
50
|
+
throw new TypeError('"target" must not be null or undefined')
|
|
51
|
+
}
|
|
52
|
+
for (const source of sources) {
|
|
53
|
+
if (source == null) {
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
for (const key in source) {
|
|
57
|
+
target[key] = source[key]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return target
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compares two values for equality
|
|
65
|
+
* 1. First checks strict equality (===),
|
|
66
|
+
* 2. then checks if either value has an `equals` method and uses it.
|
|
67
|
+
* @param {*} value1 - First value to compare
|
|
68
|
+
* @param {*} value2 - Second value to compare
|
|
69
|
+
* @returns {boolean} True if values are equal, false otherwise
|
|
70
|
+
*/
|
|
71
|
+
function equals (value1, value2) {
|
|
72
|
+
if (value1 === value2) {
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
if (typeof value1?.equals === 'function') {
|
|
76
|
+
return value1.equals(value2)
|
|
77
|
+
}
|
|
78
|
+
if (typeof value2?.equals === 'function') {
|
|
79
|
+
return value2.equals(value1)
|
|
80
|
+
}
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if the current environment is a browser
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
function isBrowser () {
|
|
89
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if the current environment is a nodejs
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
function isNode () {
|
|
97
|
+
return !isBrowser()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const LangUtils = {
|
|
101
|
+
constructorName,
|
|
102
|
+
defaults,
|
|
103
|
+
extend,
|
|
104
|
+
extends: extend,
|
|
105
|
+
equals,
|
|
106
|
+
isBrowser,
|
|
107
|
+
isNode
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = LangUtils
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @module PromiseUtils
|
|
6
|
+
* @description Promise utility functions for enhanced promise handling, including timeout, delay, parallel execution, and series execution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { assertNumber, assertPromise, assertArray, assertFunction } = require('./type-assert')
|
|
10
|
+
const { isPromise, isNumber } = require('./type-utils')
|
|
11
|
+
/**
|
|
12
|
+
* Creates a "Deferred" Object with Timeout support
|
|
13
|
+
* 1. timeout=-1, it means no timeout check
|
|
14
|
+
* @param {number} [timeout=-1] - Timeout duration in milliseconds
|
|
15
|
+
* @param {string} [timeoutMessage]
|
|
16
|
+
* @returns {{promise: Promise<*>, reject: function, resolve: function}}
|
|
17
|
+
*/
|
|
18
|
+
function defer (timeout = -1, timeoutMessage) {
|
|
19
|
+
assertNumber(timeout)
|
|
20
|
+
const rtnVal = {}
|
|
21
|
+
|
|
22
|
+
let timerHandler
|
|
23
|
+
if (timeout >= 0) {
|
|
24
|
+
rtnVal.timerHandler = timerHandler = setTimeout(() => {
|
|
25
|
+
clearTimeout(timerHandler) // must clear it
|
|
26
|
+
rtnVal.timerCleared = true // easy to check in test case
|
|
27
|
+
rtnVal.reject(new Error(timeoutMessage ?? `Promise Timeout: ${timeout}ms`))
|
|
28
|
+
}, timeout)
|
|
29
|
+
rtnVal.timerHandler = timerHandler
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
rtnVal.promise = new Promise((resolve, reject) => {
|
|
33
|
+
rtnVal.resolve = (...args) => {
|
|
34
|
+
if (timerHandler != null) {
|
|
35
|
+
clearTimeout(timerHandler) // must clear it
|
|
36
|
+
rtnVal.timerCleared = true // easy to check in test case
|
|
37
|
+
}
|
|
38
|
+
rtnVal.resolved = true
|
|
39
|
+
resolve(...args)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
rtnVal.reject = (err) => {
|
|
43
|
+
if (timerHandler != null) {
|
|
44
|
+
clearTimeout(timerHandler) // must clear it
|
|
45
|
+
rtnVal.timerCleared = true // easy to check in test case
|
|
46
|
+
}
|
|
47
|
+
rtnVal.rejected = true
|
|
48
|
+
reject(err)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
rtnVal.promise.cancel = () => {
|
|
52
|
+
if (timerHandler != null) {
|
|
53
|
+
clearTimeout(timerHandler) // must clear it
|
|
54
|
+
rtnVal.timerCleared = true // easy to check in test case
|
|
55
|
+
}
|
|
56
|
+
rtnVal.rejected = true // easy to check in test case
|
|
57
|
+
rtnVal.canceled = rtnVal.promise.canceled = true // easy to check in test case
|
|
58
|
+
rtnVal.reject(new Error('Cancelled'))
|
|
59
|
+
}
|
|
60
|
+
return rtnVal
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a timeout wrapper around a promise that rejects if the promise doesn't resolve within the given time.
|
|
65
|
+
* @param {Promise<*>} promise - The promise to wrap with a timeout
|
|
66
|
+
* @param {number} [time=1] - Timeout duration in milliseconds
|
|
67
|
+
* @param {string} [message=`Promise Timeout: ${time}ms`] - Custom rejection message
|
|
68
|
+
* @returns {Promise<*>} A new promise that either resolves with the original promise's value, rejects with original promise's error or timeout error
|
|
69
|
+
* @throws {TypeError} If input is not a promise or timeout is not a number
|
|
70
|
+
*/
|
|
71
|
+
function timeout (promise, time, message) {
|
|
72
|
+
assertPromise(promise)
|
|
73
|
+
|
|
74
|
+
time = time ?? 1
|
|
75
|
+
assertNumber(time)
|
|
76
|
+
|
|
77
|
+
const deferred = PromiseUtils.defer(time, message)
|
|
78
|
+
const startTs = Date.now()
|
|
79
|
+
promise.then((...args) => { // original promise settled, but timeout
|
|
80
|
+
const elapsed = Date.now() - startTs
|
|
81
|
+
if (elapsed <= time) {
|
|
82
|
+
deferred.resolve(...args)
|
|
83
|
+
} else {
|
|
84
|
+
deferred.reject(new Error(message ?? `Promise Timeout: ${time}ms`))
|
|
85
|
+
}
|
|
86
|
+
}).catch((err) => {
|
|
87
|
+
// prevent double reject
|
|
88
|
+
!deferred.resolved && !deferred.rejected && deferred.reject(err)
|
|
89
|
+
})
|
|
90
|
+
return deferred.promise
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Excutes All promises in parallel and returns an array of results.
|
|
95
|
+
*
|
|
96
|
+
* Why:
|
|
97
|
+
* 1. Promise.allSettled() returns
|
|
98
|
+
* * { status: 'fulfilled', value: any } when promise fulfills
|
|
99
|
+
* * { status: 'rejected', reason: any } when promise rejects
|
|
100
|
+
* 2. It's NOT convenient to use Promise.allSettled() to get the results of all promises.
|
|
101
|
+
* * the data structure is not consistent when fullfilled or rejected
|
|
102
|
+
* * have to check "string" type of status to know sucess or failure
|
|
103
|
+
* @param {Promise} promises
|
|
104
|
+
* @returns {Array<{ok: boolean, result: any}>}
|
|
105
|
+
*/
|
|
106
|
+
async function allSettled (promises) {
|
|
107
|
+
assertArray(promises)
|
|
108
|
+
const results = await Promise.allSettled(promises)
|
|
109
|
+
const rtnVal = []
|
|
110
|
+
for (const result of results) {
|
|
111
|
+
if (result.status === 'fulfilled') {
|
|
112
|
+
rtnVal.push({ ok: true, result: result.value })
|
|
113
|
+
}
|
|
114
|
+
if (result.status === 'rejected') {
|
|
115
|
+
rtnVal.push({ ok: false, result: result.reason })
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return rtnVal
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Execute the task Function, and ensure it returns a Promise.
|
|
123
|
+
* @param {function} task
|
|
124
|
+
* @returns {Promise<*>}
|
|
125
|
+
*/
|
|
126
|
+
function returnValuePromised (task) {
|
|
127
|
+
try {
|
|
128
|
+
const taskRtnVal = task()
|
|
129
|
+
if (isPromise(taskRtnVal)) {
|
|
130
|
+
return taskRtnVal
|
|
131
|
+
}
|
|
132
|
+
return Promise.resolve(taskRtnVal)
|
|
133
|
+
} catch (e) {
|
|
134
|
+
return Promise.reject(e)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Delays a promise by a specified time.
|
|
140
|
+
* 1. delay(), wait 1ms
|
|
141
|
+
* 2. delay(1000), wait 1000ms
|
|
142
|
+
* 3. delay(promise), after promise settled, wait 1000ms
|
|
143
|
+
* 4. delay(promise, 2000), after promise settled, wait 2000ms
|
|
144
|
+
*
|
|
145
|
+
* @param {Promise<*>|number|undefined} [promise] - The input promise to delay
|
|
146
|
+
* @param {number|undefined} [ms] - Minimum delay in milliseconds (default: 1)
|
|
147
|
+
* @returns {Promise} A new promise that settles after the delay period
|
|
148
|
+
*/
|
|
149
|
+
function delay (promise, ms) {
|
|
150
|
+
if (isNumber(promise)) {
|
|
151
|
+
ms = promise
|
|
152
|
+
promise = Promise.resolve()
|
|
153
|
+
} else if (promise == null && ms == null) {
|
|
154
|
+
ms = 1
|
|
155
|
+
promise = Promise.resolve()
|
|
156
|
+
}
|
|
157
|
+
promise != null && assertPromise(promise)
|
|
158
|
+
ms = ms ?? 1000
|
|
159
|
+
assertNumber(ms)
|
|
160
|
+
const deferred = PromiseUtils.defer()
|
|
161
|
+
const startTs = Date.now()
|
|
162
|
+
promise
|
|
163
|
+
.then((...args) => {
|
|
164
|
+
const escaped = Date.now() - startTs
|
|
165
|
+
if (escaped < ms) {
|
|
166
|
+
setTimeout(() => deferred.resolve(...args), ms - escaped)
|
|
167
|
+
} else {
|
|
168
|
+
deferred.resolve(...args)
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
.catch((err) => {
|
|
172
|
+
const escaped = Date.now() - startTs
|
|
173
|
+
if (escaped < ms) {
|
|
174
|
+
setTimeout(() => deferred.reject(err), ms - escaped)
|
|
175
|
+
} else {
|
|
176
|
+
deferred.reject(err)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
return deferred.promise
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Fast-Fail mode to execute Tasks(functions) in series (one after another) and returns their results in order.
|
|
184
|
+
* 1. function are executed one by one
|
|
185
|
+
* 2. Fast Fail: if any tasks fail, the whole chain is rejected with the first error
|
|
186
|
+
* 3. if an element is not function, rejects the whole chain with Error(Not Function)
|
|
187
|
+
* @param {Function[]} promises
|
|
188
|
+
* @returns {Promise<Array>} Promise that resolves with an array of results in the same order as input tasks
|
|
189
|
+
*/
|
|
190
|
+
async function series (tasks) {
|
|
191
|
+
assertArray(tasks)
|
|
192
|
+
const results = []
|
|
193
|
+
for (const task of tasks) {
|
|
194
|
+
assertFunction(task)
|
|
195
|
+
results.push(await task())
|
|
196
|
+
}
|
|
197
|
+
return results
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* AllSettled Mode to execute Tasks(functions) in series (one after another) and returns their results in order.
|
|
202
|
+
* 1. tasks are executed one by one
|
|
203
|
+
* 2. Each result is an object with `ok` (boolean) and `result` (resolved value or error).
|
|
204
|
+
* 3. if a task is not Function, rejects the whole chain with Error(Not Function)
|
|
205
|
+
* @param {Function[]} tasks
|
|
206
|
+
* @returns {Promise<Array<{ok: boolean, result: *}>>}
|
|
207
|
+
*/
|
|
208
|
+
async function seriesAllSettled (tasks) {
|
|
209
|
+
assertArray(tasks)
|
|
210
|
+
const results = []
|
|
211
|
+
for (const task of tasks) {
|
|
212
|
+
assertFunction(task)
|
|
213
|
+
try {
|
|
214
|
+
results.push({ ok: true, result: await task() })
|
|
215
|
+
} catch (err) {
|
|
216
|
+
results.push({ ok: false, result: err })
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return results
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* FastFail Mode to Execute tasks in parallel with a maximum concurrency limit
|
|
224
|
+
* 1. tasks are executed in parallel with a maximum concurrency limit
|
|
225
|
+
* 2. rejects whole chain with the first error, when first task fails
|
|
226
|
+
* @param {Function[]} tasks
|
|
227
|
+
* @param {number} [maxParallel=5]
|
|
228
|
+
* @returns {Promise<Array>} Array of resolved values from all promises
|
|
229
|
+
* @throws {TypeError} If input is not an array of function or maxParallel is not a number
|
|
230
|
+
*/
|
|
231
|
+
async function parallel (tasks, maxParallel = 5) {
|
|
232
|
+
assertArray(tasks)
|
|
233
|
+
assertNumber(maxParallel)
|
|
234
|
+
if (maxParallel <= 0) {
|
|
235
|
+
throw new Error(`Invalid maxParallel: ${maxParallel}, should > 0`)
|
|
236
|
+
}
|
|
237
|
+
tasks.forEach((task) => assertFunction(task))
|
|
238
|
+
const rtnVal = []
|
|
239
|
+
// once for all, run all tasks
|
|
240
|
+
if (tasks.length <= maxParallel) {
|
|
241
|
+
const resultsForBatch = await Promise.all(tasks.map(task => PromiseUtils.returnValuePromised(task)))
|
|
242
|
+
rtnVal.push(...resultsForBatch)
|
|
243
|
+
return rtnVal
|
|
244
|
+
}
|
|
245
|
+
// run group by MaxParallel
|
|
246
|
+
const tasksToRun = []
|
|
247
|
+
for (const task of tasks) {
|
|
248
|
+
assertFunction(task)
|
|
249
|
+
tasksToRun.push(task)
|
|
250
|
+
if (tasksToRun.length >= maxParallel) {
|
|
251
|
+
const resultsForBatch = await Promise.all(tasksToRun.map(task => PromiseUtils.returnValuePromised(task)))
|
|
252
|
+
rtnVal.push(...resultsForBatch)
|
|
253
|
+
tasksToRun.length = 0
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Run all rested
|
|
257
|
+
if (tasksToRun.length > 0 && tasksToRun.length < maxParallel) {
|
|
258
|
+
const resultsForBatch = await Promise.all(tasksToRun.map(task => PromiseUtils.returnValuePromised(task)))
|
|
259
|
+
rtnVal.push(...resultsForBatch)
|
|
260
|
+
}
|
|
261
|
+
return rtnVal
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* AllSettled Mode to execute tasks in parallel with a maximum concurrency limit
|
|
266
|
+
* 1. tasks are executed in parallel with a maximum concurrency limit
|
|
267
|
+
* 2. all tasks will be executed, even some of them failed.
|
|
268
|
+
* @param {Function[]} tasks
|
|
269
|
+
* @param {number} [maxParallel=5] - Maximum number of tasks to run in parallel
|
|
270
|
+
* @returns {Promise<Array>} Array of resolved values from all promises
|
|
271
|
+
* @throws {TypeError} If input is not an array of function or maxParallel is not a number
|
|
272
|
+
*/
|
|
273
|
+
async function parallelAllSettled (tasks, maxParallel = 5) {
|
|
274
|
+
assertArray(tasks)
|
|
275
|
+
assertNumber(maxParallel)
|
|
276
|
+
if (maxParallel <= 0) {
|
|
277
|
+
throw new Error(`Invalid maxParallel: ${maxParallel}, should > 0`)
|
|
278
|
+
}
|
|
279
|
+
tasks.forEach((task) => assertFunction(task))
|
|
280
|
+
const rtnVal = []
|
|
281
|
+
// once for all, run all promises
|
|
282
|
+
if (tasks.length <= maxParallel) {
|
|
283
|
+
const resultsForBatch = await PromiseUtils.allSettled(tasks.map(task => PromiseUtils.returnValuePromised(task)))
|
|
284
|
+
rtnVal.push(...resultsForBatch)
|
|
285
|
+
return rtnVal
|
|
286
|
+
}
|
|
287
|
+
// run group by MaxParallel
|
|
288
|
+
const tasksToRun = []
|
|
289
|
+
for (const task of tasks) {
|
|
290
|
+
assertFunction(task)
|
|
291
|
+
tasksToRun.push(task)
|
|
292
|
+
if (tasksToRun.length >= maxParallel) {
|
|
293
|
+
const resultsForBatch = await PromiseUtils.allSettled(tasksToRun.map(task => PromiseUtils.returnValuePromised(task)))
|
|
294
|
+
rtnVal.push(...resultsForBatch)
|
|
295
|
+
tasksToRun.length = 0
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Run all rested
|
|
299
|
+
if (tasksToRun.length > 0 && tasksToRun.length < maxParallel) {
|
|
300
|
+
const resultsForBatch = await PromiseUtils.allSettled(tasksToRun.map(task => PromiseUtils.returnValuePromised(task)))
|
|
301
|
+
rtnVal.push(...resultsForBatch)
|
|
302
|
+
}
|
|
303
|
+
return rtnVal
|
|
304
|
+
}
|
|
305
|
+
const PromiseUtils = {
|
|
306
|
+
defer,
|
|
307
|
+
delay,
|
|
308
|
+
timeout,
|
|
309
|
+
allSettled,
|
|
310
|
+
returnValuePromised,
|
|
311
|
+
series,
|
|
312
|
+
seriesAllSettled,
|
|
313
|
+
parallel,
|
|
314
|
+
parallelAllSettled
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
module.exports = PromiseUtils
|