@parallel-park/run-jobs 0.3.0 → 0.3.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.
@@ -0,0 +1,13 @@
1
+ declare let debug: null | ((...args: any) => void);
2
+ export declare function setDebug(debugFunction: typeof debug): void;
3
+ export declare function runJobs<T, U>(inputs: Iterable<T | Promise<T>> | AsyncIterable<T | Promise<T>>, mapper: (input: T, index: number, length: number) => Promise<U>, {
4
+ /**
5
+ * How many jobs are allowed to run at once.
6
+ */
7
+ concurrency, }?: {
8
+ /**
9
+ * How many jobs are allowed to run at once.
10
+ */
11
+ concurrency?: number;
12
+ }): Promise<Array<U>>;
13
+ export {};
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@parallel-park/run-jobs",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Parallel async work with an optional concurrency limit, like Bluebird's Promise.map",
5
5
  "main": "dist/index.js",
6
- "types": "src/index.ts",
6
+ "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "test": "vitest run",
9
9
  "build": "rm -rf ./dist && tsc"
package/src/run-jobs.ts DELETED
@@ -1,159 +0,0 @@
1
- let debug: null | ((...args: any) => void) = null;
2
-
3
- export function setDebug(debugFunction: typeof debug) {
4
- debug = debugFunction;
5
- }
6
-
7
- function isThenable<T>(value: unknown): value is Promise<T> {
8
- return (
9
- typeof value === "object" &&
10
- value != null &&
11
- // @ts-ignore accessing .then
12
- typeof value.then === "function"
13
- );
14
- }
15
-
16
- const NOTHING = Symbol("NOTHING");
17
-
18
- let runJobsCallId = 0;
19
-
20
- export async function runJobs<T, U>(
21
- inputs: Iterable<T | Promise<T>> | AsyncIterable<T | Promise<T>>,
22
- mapper: (input: T, index: number, length: number) => Promise<U>,
23
- {
24
- /**
25
- * How many jobs are allowed to run at once.
26
- */
27
- concurrency = 8,
28
- }: {
29
- /**
30
- * How many jobs are allowed to run at once.
31
- */
32
- concurrency?: number;
33
- } = {}
34
- ): Promise<Array<U>> {
35
- const callId = runJobsCallId;
36
- runJobsCallId++;
37
-
38
- if (debug) {
39
- debug(`runJobs called (callId: ${callId})`, {
40
- inputs,
41
- mapper,
42
- concurrency,
43
- });
44
- }
45
-
46
- if (concurrency < 1) {
47
- throw new Error(
48
- "Concurrency can't be less than one; that doesn't make any sense."
49
- );
50
- }
51
-
52
- const inputsArray: Array<T> = [];
53
- const inputIteratorFactory =
54
- inputs[Symbol.asyncIterator || NOTHING] || inputs[Symbol.iterator];
55
- const inputIterator = inputIteratorFactory.call(inputs);
56
- const maybeLength = Array.isArray(inputs) ? inputs.length : null;
57
-
58
- let iteratorDone = false;
59
-
60
- async function readInput(): Promise<boolean> {
61
- if (debug) {
62
- debug(`reading next input (callId: ${callId})`);
63
- }
64
- let nextResult = inputIterator.next();
65
- if (isThenable(nextResult)) {
66
- nextResult = await nextResult;
67
- }
68
- if (nextResult.done) {
69
- iteratorDone = true;
70
- return false;
71
- } else {
72
- let value = nextResult.value;
73
- if (isThenable<T>(value)) {
74
- value = await value;
75
- }
76
- inputsArray.push(value);
77
- return true;
78
- }
79
- }
80
-
81
- let unstartedIndex = 0;
82
-
83
- const results = new Array(maybeLength || 0);
84
- const runningPromises = new Set();
85
- let error: Error | null = null;
86
-
87
- async function takeInput() {
88
- const read = await readInput();
89
- if (!read) return;
90
-
91
- const inputIndex = unstartedIndex;
92
- unstartedIndex++;
93
-
94
- const input = inputsArray[inputIndex];
95
- if (debug) {
96
- debug(`mapping input into Promise (callId: ${callId})`);
97
- }
98
- const promise = mapper(input, inputIndex, maybeLength || Infinity);
99
-
100
- if (!isThenable(promise)) {
101
- throw new Error(
102
- "Mapper function passed into runJobs didn't return a Promise. The mapper function should always return a Promise. The easiest way to ensure this is the case is to make your mapper function an async function."
103
- );
104
- }
105
-
106
- const promiseWithMore = promise.then(
107
- (result) => {
108
- if (debug) {
109
- debug(`child Promise resolved for input (callId: ${callId}):`, input);
110
- }
111
- results[inputIndex] = result;
112
- runningPromises.delete(promiseWithMore);
113
- },
114
- (err) => {
115
- if (debug) {
116
- debug(
117
- `child Promise rejected for input (callId: ${callId}):`,
118
- input,
119
- "with error:",
120
- err
121
- );
122
- }
123
- runningPromises.delete(promiseWithMore);
124
- error = err;
125
- }
126
- );
127
- runningPromises.add(promiseWithMore);
128
- }
129
-
130
- async function proceed() {
131
- while (!iteratorDone && runningPromises.size < concurrency) {
132
- await takeInput();
133
- }
134
- }
135
-
136
- await proceed();
137
- while (runningPromises.size > 0 && !error) {
138
- await Promise.race(runningPromises.values());
139
- if (error) {
140
- if (debug) {
141
- debug(`throwing error (callId: ${callId})`);
142
- }
143
- throw error;
144
- }
145
- await proceed();
146
- }
147
-
148
- if (error) {
149
- if (debug) {
150
- debug(`throwing error (callId: ${callId})`);
151
- }
152
- throw error;
153
- }
154
-
155
- if (debug) {
156
- debug(`all done (callId: ${callId})`);
157
- }
158
- return results;
159
- }
File without changes