@stdlib/utils-async-parallel 0.1.0

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.
@@ -0,0 +1,312 @@
1
+ /*
2
+ * @license Apache-2.0
3
+ *
4
+ * Copyright (c) 2024 The Stdlib Authors.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ // TypeScript Version: 4.1
20
+
21
+ /**
22
+ * Interface defining function options.
23
+ */
24
+ interface Options<T> {
25
+ /**
26
+ * The maximum number of pending invocations at any one time.
27
+ */
28
+ limit?: number;
29
+
30
+ /**
31
+ * Execution context.
32
+ */
33
+ thisArg?: ThisParameterType<Callback<T>>;
34
+ }
35
+
36
+ /**
37
+ * Callback invoked upon completion.
38
+ */
39
+ type Nullary = () => void;
40
+
41
+ /**
42
+ * Callback invoked upon completion.
43
+ *
44
+ * @param error - encountered error
45
+ */
46
+ type Unary = ( error: Error ) => void;
47
+
48
+ /**
49
+ * Callback invoked upon completion.
50
+ *
51
+ * @param error - encountered error
52
+ * @param out - output results
53
+ */
54
+ type Binary<T> = ( error: Error | null, out: Array<T> ) => void;
55
+
56
+ /**
57
+ * Callback invoked upon completion.
58
+ *
59
+ * @param error - encountered error
60
+ * @param out - output results
61
+ */
62
+ type Callback<T> = Nullary | Unary | Binary<T>;
63
+
64
+ /**
65
+ * Executes a set of functions in parallel.
66
+ */
67
+ type ParallelFunction<T> = ( done: Callback<T> ) => void;
68
+
69
+ /**
70
+ * Interface for `parallel`.
71
+ */
72
+ interface Parallel {
73
+ /**
74
+ * Executes a set of functions in parallel.
75
+ *
76
+ * @param fcns - array of functions
77
+ * @param options - function options
78
+ * @param options.thisArg - function context
79
+ * @param options.limit - number of functions to execute concurrently
80
+ * @param clbk - callback to invoke upon completion
81
+ *
82
+ * @example
83
+ * var parallel = require( '@stdlib/utils-async-parallel' );
84
+ *
85
+ * function foo( clbk ) {
86
+ * setTimeout( onTimeout, 300 );
87
+ * function onTimeout() {
88
+ * clbk( null, 'one' );
89
+ * }
90
+ * }
91
+ *
92
+ * function bar( clbk ) {
93
+ * setTimeout( onTimeout, 100 );
94
+ * function onTimeout() {
95
+ * clbk( null, 'two' );
96
+ * }
97
+ * }
98
+ *
99
+ * function done( error, results ) {
100
+ * if ( error ) {
101
+ * throw error;
102
+ * }
103
+ * console.log( results );
104
+ * // => [ 'one', 'two' ]
105
+ * }
106
+ *
107
+ * var fcns = [ foo, bar ];
108
+ *
109
+ * var opts = {
110
+ * 'thisArg': {},
111
+ * 'limit': 2
112
+ * }
113
+ *
114
+ * parallel( fcns, opts, done );
115
+ */
116
+ <T = unknown>( fcns: ArrayLike<Function>, options: Options<T>, clbk: Callback<T> ): void;
117
+
118
+ /**
119
+ * Executes a set of functions in parallel.
120
+ *
121
+ * @param fcns - array of functions
122
+ * @param clbk - callback to invoke upon completion
123
+ *
124
+ * @example
125
+ * var parallel = require( '@stdlib/utils-async-parallel' );
126
+ *
127
+ * function foo( clbk ) {
128
+ * setTimeout( onTimeout, 300 );
129
+ * function onTimeout() {
130
+ * clbk( null, 'one' );
131
+ * }
132
+ * }
133
+ *
134
+ * function bar( clbk ) {
135
+ * setTimeout( onTimeout, 100 );
136
+ * function onTimeout() {
137
+ * clbk( null, 'two' );
138
+ * }
139
+ * }
140
+ *
141
+ * function done( error, results ) {
142
+ * if ( error ) {
143
+ * throw error;
144
+ * }
145
+ * console.log( results );
146
+ * // => [ 'one', 'two' ]
147
+ * }
148
+ *
149
+ * var fcns = [ foo, bar ];
150
+ *
151
+ * parallel( fcns, done );
152
+ */
153
+ <T = unknown>( fcns: ArrayLike<Function>, clbk: Callback<T> ): void;
154
+
155
+ /**
156
+ * Returns a reusable parallel function.
157
+ *
158
+ * @param fcns - array of functions
159
+ * @param options - function options
160
+ * @param options.thisArg - function context
161
+ * @param options.limit - number of functions to execute concurrently
162
+ * @param clbk - callback to invoke upon completion
163
+ * @returns parallel function
164
+ *
165
+ * @example
166
+ * function a( clbk ) {
167
+ * setTimeout( onTimeout, 0 );
168
+ * function onTimeout() {
169
+ * clbk( null, 2 );
170
+ * }
171
+ * }
172
+ *
173
+ * function b( clbk ) {
174
+ * setTimeout( onTimeout, 0 );
175
+ * function onTimeout() {
176
+ * clbk( null, 4 );
177
+ * }
178
+ * }
179
+ *
180
+ * function done( error, out ) {
181
+ * if ( error ) {
182
+ * throw error;
183
+ * }
184
+ * console.log( out );
185
+ * // => [ 2, 4 ]
186
+ * }
187
+ *
188
+ * var fcns = [ a, b ];
189
+ * var run = parallel.factory( fcns );
190
+ *
191
+ * // ...
192
+ *
193
+ * run( done );
194
+ *
195
+ * @example
196
+ * function a( clbk ) {
197
+ * setTimeout( onTimeout, 0 );
198
+ * function onTimeout() {
199
+ * clbk( null, 2 );
200
+ * }
201
+ * }
202
+ *
203
+ * function b( clbk ) {
204
+ * setTimeout( onTimeout, 0 );
205
+ * function onTimeout() {
206
+ * clbk( null, 4 );
207
+ * }
208
+ * }
209
+ *
210
+ * function done( error, out ) {
211
+ * if ( error ) {
212
+ * throw error;
213
+ * }
214
+ * console.log( out );
215
+ * // => [ 2, 4 ]
216
+ * }
217
+ *
218
+ * var fcns = [ a, b ];
219
+ *
220
+ * var opts = {
221
+ * 'thisArg': {},
222
+ * 'limit': 2
223
+ * }
224
+ *
225
+ * var run = parallel.factory( fcns, opts );
226
+ *
227
+ * // ...
228
+ *
229
+ * run( done );
230
+ */
231
+ factory<T = unknown>( fcns: ArrayLike<Function>, options?: Options<T> ): ParallelFunction<T>;
232
+ }
233
+
234
+ /**
235
+ * Executes a set of functions in parallel and passes the results of all functions to a provided callback.
236
+ *
237
+ * ## Notes
238
+ *
239
+ * - This function is intended to start asynchronous tasks so that execution of each task runs concurrently. If provided a function which does not perform asynchronous tasks, the function will execute synchronously.
240
+ * - The function executes provided functions in the same thread. Accordingly, the function does **not** spawn new threads.
241
+ *
242
+ * @param fcns - array of functions
243
+ * @param options - function options
244
+ * @param options.thisArg - function context
245
+ * @param options.limit - number of functions to execute concurrently
246
+ * @param clbk - callback to invoke upon completion
247
+ *
248
+ * @example
249
+ * var parallel = require( '@stdlib/utils-async-parallel' );
250
+ *
251
+ * function foo( clbk ) {
252
+ * setTimeout( onTimeout, 300 );
253
+ * function onTimeout() {
254
+ * clbk( null, 'one' );
255
+ * }
256
+ * }
257
+ *
258
+ * function bar( clbk ) {
259
+ * setTimeout( onTimeout, 100 );
260
+ * function onTimeout() {
261
+ * clbk( null, 'two' );
262
+ * }
263
+ * }
264
+ *
265
+ * function done( error, results ) {
266
+ * if ( error ) {
267
+ * throw error;
268
+ * }
269
+ * console.log( results );
270
+ * // => [ 'one', 'two' ]
271
+ * }
272
+ *
273
+ * var fcns = [ foo, bar ];
274
+ *
275
+ * parallel( fcns, done );
276
+ *
277
+ * @example
278
+ * function a( clbk ) {
279
+ * setTimeout( onTimeout, 0 );
280
+ * function onTimeout() {
281
+ * clbk( null, 2 );
282
+ * }
283
+ * }
284
+ *
285
+ * function b( clbk ) {
286
+ * setTimeout( onTimeout, 0 );
287
+ * function onTimeout() {
288
+ * clbk( null, 4 );
289
+ * }
290
+ * }
291
+ *
292
+ * function done( error, out ) {
293
+ * if ( error ) {
294
+ * throw error;
295
+ * }
296
+ * console.log( out );
297
+ * // => [ 2, 4 ]
298
+ * }
299
+ *
300
+ * var fcns = [ a, b ];
301
+ * var run = parallel.factory( fcns );
302
+ *
303
+ * // ...
304
+ *
305
+ * run( done );
306
+ */
307
+ declare var parallel: Parallel;
308
+
309
+
310
+ // EXPORTS //
311
+
312
+ export = parallel;
package/lib/factory.js ADDED
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @license Apache-2.0
3
+ *
4
+ * Copyright (c) 2024 The Stdlib Authors.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ // MODULES //
22
+
23
+ var isFunctionArray = require( '@stdlib/assert-is-function-array' );
24
+ var isFunction = require( '@stdlib/assert-is-function' );
25
+ var format = require( '@stdlib/string-format' );
26
+ var PINF = require( '@stdlib/constants-float64-pinf' );
27
+ var validate = require( './validate.js' );
28
+ var limit = require( './limit.js' );
29
+
30
+
31
+ // MAIN //
32
+
33
+ /**
34
+ * Returns a function to execute a set of functions in parallel.
35
+ *
36
+ * @param {FunctionArray} fcns - array of functions
37
+ * @param {Options} [options] - function options
38
+ * @param {*} [options.thisArg] - execution context
39
+ * @param {PositiveInteger} [options.limit] - maximum number of pending invocations at any one time
40
+ * @throws {TypeError} first argument must be an array of functions
41
+ * @throws {TypeError} options argument must be an object
42
+ * @throws {TypeError} must provide valid options
43
+ * @returns {Function} parallel function
44
+ *
45
+ * @example
46
+ * function a( resolve ) {
47
+ * setTimeout( onTimeout, 0 );
48
+ * function onTimeout() {
49
+ * resolve( null, 2 );
50
+ * }
51
+ * }
52
+ *
53
+ * function b( resolve ) {
54
+ * setTimeout( onTimeout, 0 );
55
+ * function onTimeout() {
56
+ * resolve( null, 4 );
57
+ * }
58
+ * }
59
+ *
60
+ * function done( error, out ) {
61
+ * if ( error ) {
62
+ * throw error;
63
+ * }
64
+ * console.log( out );
65
+ * // => [ 2, 4 ]
66
+ * }
67
+ *
68
+ * var fcns = [ a, b ];
69
+ *
70
+ * var run = parallel.factory( fcns );
71
+ *
72
+ * run( done );
73
+ */
74
+ function factory( fcns, options ) {
75
+ var opts;
76
+ var err;
77
+
78
+ if ( !isFunctionArray( fcns ) ) {
79
+ throw new TypeError( format( 'invalid argument. First argument must be an array of functions. Value: `%s`.', fcns ) );
80
+ }
81
+ opts = {
82
+ 'limit': PINF
83
+ };
84
+ if ( arguments.length > 1 ) {
85
+ err = validate( opts, options );
86
+ if ( err ) {
87
+ throw err;
88
+ }
89
+ }
90
+ return parallel;
91
+
92
+ /**
93
+ * Executes a set of functions in parallel and returns an array of results.
94
+ *
95
+ * @private
96
+ * @param {Callback} done - function to invoke upon completion
97
+ * @throws {TypeError} must provide a function
98
+ * @returns {void}
99
+ */
100
+ function parallel( done ) {
101
+ if ( !isFunction( done ) ) {
102
+ throw new TypeError( format( 'invalid argument. Callback argument must be a function. Value: `%s`.', done ) );
103
+ }
104
+ return limit( fcns, opts, clbk );
105
+
106
+ /**
107
+ * Callback invoked upon completion.
108
+ *
109
+ * @private
110
+ * @param {*} [error] - error
111
+ * @param {Array} [out] - output array
112
+ * @returns {void}
113
+ */
114
+ function clbk( error, out ) {
115
+ if ( error ) {
116
+ return done( error );
117
+ }
118
+ done( null, out );
119
+ }
120
+ }
121
+ }
122
+
123
+
124
+ // EXPORTS //
125
+
126
+ module.exports = factory;
package/lib/index.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @license Apache-2.0
3
+ *
4
+ * Copyright (c) 2024 The Stdlib Authors.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ /**
22
+ * Execute a set of functions in parallel.
23
+ *
24
+ * @module @stdlib/utils-async-parallel
25
+ *
26
+ * @example
27
+ * var parallel = require( '@stdlib/utils-async-parallel' );
28
+ *
29
+ * function foo( resolve ) {
30
+ * setTimeout( onTimeout, 300 );
31
+ * function onTimeout() {
32
+ * resolve( null, 'one' );
33
+ * }
34
+ * }
35
+ *
36
+ * function bar( resolve ) {
37
+ * setTimeout( onTimeout, 100 );
38
+ * function onTimeout() {
39
+ * resolve( null, 'two' );
40
+ * }
41
+ * }
42
+ *
43
+ * function done( error, results ) {
44
+ * if ( error ) {
45
+ * throw error;
46
+ * }
47
+ * console.log( results );
48
+ * // => [ 'one', 'two' ]
49
+ * }
50
+ *
51
+ * var fcns = [ foo, bar ];
52
+ *
53
+ * parallel( fcns, done );
54
+ */
55
+
56
+ // MODULES //
57
+
58
+ var setReadOnly = require( '@stdlib/utils-define-nonenumerable-read-only-property' );
59
+ var main = require( './main.js' );
60
+ var factory = require( './factory.js' );
61
+
62
+
63
+ // MAIN //
64
+
65
+ setReadOnly( main, 'factory', factory );
66
+
67
+
68
+ // EXPORTS //
69
+
70
+ module.exports = main;
package/lib/limit.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @license Apache-2.0
3
+ *
4
+ * Copyright (c) 2024 The Stdlib Authors.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ // MODULES //
22
+
23
+ var logger = require( 'debug' );
24
+
25
+
26
+ // VARIABLES //
27
+
28
+ var debug = logger( 'parallel-async:limit' );
29
+
30
+
31
+ // MAIN //
32
+
33
+ /**
34
+ * Invokes functions in a provided array, limiting the number of concurrently pending functions.
35
+ *
36
+ * @private
37
+ * @param {FunctionArray} fcns - array of functions
38
+ * @param {Options} opts - function options
39
+ * @param {*} [opts.thisArg] - execution context
40
+ * @param {PositiveInteger} [opts.limit] - maximum number of pending function invocations
41
+ * @param {Callback} done - function to invoke upon completion or upon encountering an error
42
+ * @returns {void}
43
+ */
44
+ function limit( fcns, opts, done ) {
45
+ var maxIndex;
46
+ var count;
47
+ var flg;
48
+ var lim;
49
+ var len;
50
+ var idx;
51
+ var out;
52
+ var i;
53
+
54
+ len = fcns.length;
55
+ debug( 'Number of functions: %d', len );
56
+
57
+ out = new Array( len );
58
+ if ( len < opts.limit ) {
59
+ lim = len;
60
+ } else {
61
+ lim = opts.limit;
62
+ }
63
+ debug( 'Concurrency limit: %d', lim );
64
+
65
+ maxIndex = len - 1;
66
+ count = 0;
67
+ idx = -1;
68
+ for ( i = 0; i < lim; i++ ) {
69
+ // This guard is necessary to protect against synchronous functions...
70
+ if ( idx < maxIndex ) {
71
+ next(); // eslint-disable-line node/callback-return
72
+ }
73
+ }
74
+ /**
75
+ * Callback to invoke the next function.
76
+ *
77
+ * @private
78
+ */
79
+ function next() {
80
+ var i;
81
+
82
+ idx += 1;
83
+
84
+ // Cache the current index value to allow storing results later:
85
+ i = idx;
86
+
87
+ fcns[ idx ].call( opts.thisArg, resolve );
88
+
89
+ /**
90
+ * Callback invoked once a provided function finishes.
91
+ *
92
+ * @private
93
+ * @param {*} [error] - error
94
+ * @param {*} [results] - results
95
+ * @returns {void}
96
+ */
97
+ function resolve( error, results ) {
98
+ if ( flg ) {
99
+ // Prevent further processing:
100
+ return;
101
+ }
102
+ if ( error ) {
103
+ flg = true;
104
+ return clbk( error );
105
+ }
106
+ out[ i ] = results;
107
+ clbk();
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Callback invoked once ready to process the next function.
113
+ *
114
+ * @private
115
+ * @param {*} [error] - error
116
+ * @returns {void}
117
+ */
118
+ function clbk( error ) {
119
+ if ( error ) {
120
+ debug( 'Encountered an error: %s', error.message );
121
+ return done( error );
122
+ }
123
+ count += 1;
124
+ debug( 'Processed %d of %d functions.', count, len );
125
+ if ( idx < maxIndex ) {
126
+ return next();
127
+ }
128
+ if ( count === len ) {
129
+ debug( 'Finished processing the functions.' );
130
+ return done( null, out );
131
+ }
132
+ }
133
+ }
134
+
135
+
136
+ // EXPORTS //
137
+
138
+ module.exports = limit;
package/lib/main.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @license Apache-2.0
3
+ *
4
+ * Copyright (c) 2024 The Stdlib Authors.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ // MODULES //
22
+
23
+ var factory = require( './factory.js' );
24
+
25
+
26
+ // MAIN //
27
+
28
+ /**
29
+ * Executes a set of functions in parallel.
30
+ *
31
+ * @param {FunctionArray} fcns - array of functions
32
+ * @param {Options} [options] - function options
33
+ * @param {*} [options.thisArg] - execution context
34
+ * @param {PositiveInteger} [options.limit] - maximum number of pending invocations at any one time
35
+ * @param {Callback} done - function to invoke upon completion
36
+ * @throws {TypeError} first argument must be an array of functions
37
+ * @throws {TypeError} options argument must be an object
38
+ * @throws {TypeError} must provide valid options
39
+ * @throws {TypeError} callback argument must be a function
40
+ * @returns {void}
41
+ *
42
+ * @example
43
+ * var parallel = require( '@stdlib/utils-async-parallel' );
44
+ *
45
+ * function foo( resolve ) {
46
+ * setTimeout( onTimeout, 300 );
47
+ * function onTimeout() {
48
+ * resolve( null, 'one' );
49
+ * }
50
+ * }
51
+ *
52
+ * function bar( resolve ) {
53
+ * setTimeout( onTimeout, 100 );
54
+ * function onTimeout() {
55
+ * resolve( null, 'two' );
56
+ * }
57
+ * }
58
+ *
59
+ * function done( error, results ) {
60
+ * if ( error ) {
61
+ * throw error;
62
+ * }
63
+ * console.log( results );
64
+ * // => [ 'one', 'two' ]
65
+ * }
66
+ *
67
+ * var fcns = [ foo, bar ];
68
+ *
69
+ * parallel( fcns, done );
70
+ */
71
+ function parallel( fcns, options, done ) {
72
+ if ( arguments.length < 3 ) {
73
+ return factory( fcns )( options );
74
+ }
75
+ factory( fcns, options )( done );
76
+ }
77
+
78
+
79
+ // EXPORTS //
80
+
81
+ module.exports = parallel;