@fuzdev/fuz_util 0.45.2 → 0.45.3

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/dist/async.d.ts CHANGED
@@ -19,6 +19,24 @@ export interface Deferred<T> {
19
19
  * Creates a object with a `promise` and its `resolve`/`reject` handlers.
20
20
  */
21
21
  export declare const create_deferred: <T>() => Deferred<T>;
22
+ /**
23
+ * Runs an async function on each item with controlled concurrency.
24
+ * Like `map_concurrent` but doesn't collect results (more efficient for side effects).
25
+ *
26
+ * @param items array of items to process
27
+ * @param fn async function to apply to each item
28
+ * @param concurrency maximum number of concurrent operations
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * await each_concurrent(
33
+ * file_paths,
34
+ * async (path) => { await unlink(path); },
35
+ * 5, // max 5 concurrent deletions
36
+ * );
37
+ * ```
38
+ */
39
+ export declare const each_concurrent: <T>(items: Array<T>, fn: (item: T, index: number) => Promise<void>, concurrency: number) => Promise<void>;
22
40
  /**
23
41
  * Maps over items with controlled concurrency, preserving input order.
24
42
  *
@@ -1 +1 @@
1
- {"version":3,"file":"async.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/async.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,iBAAY,KAAG,OAAO,CAAC,IAAI,CACQ,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,OAAO,CAAC,OAAO,CACI,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,OAAK,QAAQ,CAAC,CAAC,CAQ/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,GAAU,CAAC,EAAE,CAAC,EACxC,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,aAAa,MAAM,KACjB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAoDlB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,GAAU,CAAC,EAAE,CAAC,EAChD,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,aAAa,MAAM,KACjB,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CA6CxC,CAAC"}
1
+ {"version":3,"file":"async.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/async.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,iBAAY,KAAG,OAAO,CAAC,IAAI,CACQ,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,OAAO,CAAC,OAAO,CACI,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,OAAK,QAAQ,CAAC,CAAC,CAQ/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,eAAe,GAAU,CAAC,EACtC,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAC7C,aAAa,MAAM,KACjB,OAAO,CAAC,IAAI,CAgDd,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,GAAU,CAAC,EAAE,CAAC,EACxC,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,aAAa,MAAM,KACjB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAkDlB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,GAAU,CAAC,EAAE,CAAC,EAChD,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,aAAa,MAAM,KACjB,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CA6CxC,CAAC"}
package/dist/async.js CHANGED
@@ -18,6 +18,68 @@ export const create_deferred = () => {
18
18
  });
19
19
  return { promise, resolve, reject };
20
20
  };
21
+ /**
22
+ * Runs an async function on each item with controlled concurrency.
23
+ * Like `map_concurrent` but doesn't collect results (more efficient for side effects).
24
+ *
25
+ * @param items array of items to process
26
+ * @param fn async function to apply to each item
27
+ * @param concurrency maximum number of concurrent operations
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * await each_concurrent(
32
+ * file_paths,
33
+ * async (path) => { await unlink(path); },
34
+ * 5, // max 5 concurrent deletions
35
+ * );
36
+ * ```
37
+ */
38
+ export const each_concurrent = async (items, fn, concurrency) => {
39
+ if (concurrency < 1) {
40
+ throw new Error('concurrency must be at least 1');
41
+ }
42
+ let next_index = 0;
43
+ let active_count = 0;
44
+ let rejected = false;
45
+ return new Promise((resolve, reject) => {
46
+ const run_next = () => {
47
+ // Stop spawning if we've rejected
48
+ if (rejected)
49
+ return;
50
+ // Check if we're done
51
+ if (next_index >= items.length && active_count === 0) {
52
+ resolve();
53
+ return;
54
+ }
55
+ // Spawn workers up to concurrency limit
56
+ while (active_count < concurrency && next_index < items.length) {
57
+ const index = next_index++;
58
+ const item = items[index];
59
+ active_count++;
60
+ fn(item, index)
61
+ .then(() => {
62
+ if (rejected)
63
+ return;
64
+ active_count--;
65
+ run_next();
66
+ })
67
+ .catch((error) => {
68
+ if (rejected)
69
+ return;
70
+ rejected = true;
71
+ reject(error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
72
+ });
73
+ }
74
+ };
75
+ // Handle empty array
76
+ if (items.length === 0) {
77
+ resolve();
78
+ return;
79
+ }
80
+ run_next();
81
+ });
82
+ };
21
83
  /**
22
84
  * Maps over items with controlled concurrency, preserving input order.
23
85
  *
@@ -43,7 +105,6 @@ export const map_concurrent = async (items, fn, concurrency) => {
43
105
  let next_index = 0;
44
106
  let active_count = 0;
45
107
  let rejected = false;
46
- let reject_error;
47
108
  return new Promise((resolve, reject) => {
48
109
  const run_next = () => {
49
110
  // Stop spawning if we've rejected
@@ -71,8 +132,7 @@ export const map_concurrent = async (items, fn, concurrency) => {
71
132
  if (rejected)
72
133
  return;
73
134
  rejected = true;
74
- reject_error = error;
75
- reject(reject_error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
135
+ reject(error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
76
136
  });
77
137
  }
78
138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_util",
3
- "version": "0.45.2",
3
+ "version": "0.45.3",
4
4
  "description": "utility belt for JS",
5
5
  "glyph": "🦕",
6
6
  "logo": "logo.svg",
package/src/lib/async.ts CHANGED
@@ -34,6 +34,77 @@ export const create_deferred = <T>(): Deferred<T> => {
34
34
  return {promise, resolve, reject};
35
35
  };
36
36
 
37
+ /**
38
+ * Runs an async function on each item with controlled concurrency.
39
+ * Like `map_concurrent` but doesn't collect results (more efficient for side effects).
40
+ *
41
+ * @param items array of items to process
42
+ * @param fn async function to apply to each item
43
+ * @param concurrency maximum number of concurrent operations
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * await each_concurrent(
48
+ * file_paths,
49
+ * async (path) => { await unlink(path); },
50
+ * 5, // max 5 concurrent deletions
51
+ * );
52
+ * ```
53
+ */
54
+ export const each_concurrent = async <T>(
55
+ items: Array<T>,
56
+ fn: (item: T, index: number) => Promise<void>,
57
+ concurrency: number,
58
+ ): Promise<void> => {
59
+ if (concurrency < 1) {
60
+ throw new Error('concurrency must be at least 1');
61
+ }
62
+
63
+ let next_index = 0;
64
+ let active_count = 0;
65
+ let rejected = false;
66
+
67
+ return new Promise((resolve, reject) => {
68
+ const run_next = (): void => {
69
+ // Stop spawning if we've rejected
70
+ if (rejected) return;
71
+
72
+ // Check if we're done
73
+ if (next_index >= items.length && active_count === 0) {
74
+ resolve();
75
+ return;
76
+ }
77
+
78
+ // Spawn workers up to concurrency limit
79
+ while (active_count < concurrency && next_index < items.length) {
80
+ const index = next_index++;
81
+ const item = items[index]!;
82
+ active_count++;
83
+
84
+ fn(item, index)
85
+ .then(() => {
86
+ if (rejected) return;
87
+ active_count--;
88
+ run_next();
89
+ })
90
+ .catch((error) => {
91
+ if (rejected) return;
92
+ rejected = true;
93
+ reject(error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
94
+ });
95
+ }
96
+ };
97
+
98
+ // Handle empty array
99
+ if (items.length === 0) {
100
+ resolve();
101
+ return;
102
+ }
103
+
104
+ run_next();
105
+ });
106
+ };
107
+
37
108
  /**
38
109
  * Maps over items with controlled concurrency, preserving input order.
39
110
  *
@@ -64,7 +135,6 @@ export const map_concurrent = async <T, R>(
64
135
  let next_index = 0;
65
136
  let active_count = 0;
66
137
  let rejected = false;
67
- let reject_error: unknown;
68
138
 
69
139
  return new Promise((resolve, reject) => {
70
140
  const run_next = (): void => {
@@ -93,8 +163,7 @@ export const map_concurrent = async <T, R>(
93
163
  .catch((error) => {
94
164
  if (rejected) return;
95
165
  rejected = true;
96
- reject_error = error;
97
- reject(reject_error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
166
+ reject(error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
98
167
  });
99
168
  }
100
169
  };