@chriscdn/promise-semaphore 2.0.9 → 2.0.11
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 +60 -51
- package/__tests__/semaphore.test.ts +127 -100
- package/lib/index.d.ts +1 -6
- package/lib/promise-semaphore.cjs +1 -1
- package/lib/promise-semaphore.cjs.map +1 -1
- package/lib/promise-semaphore.modern.js +1 -1
- package/lib/promise-semaphore.modern.js.map +1 -1
- package/lib/promise-semaphore.module.js +1 -1
- package/lib/promise-semaphore.module.js.map +1 -1
- package/package.json +7 -8
- package/src/index.ts +32 -11
- package/tsconfig.json +5 -0
- package/jest.config.js +0 -5
- package/lib/promise-semaphore.umd.js +0 -3
- package/lib/promise-semaphore.umd.js.map +0 -1
package/README.md
CHANGED
|
@@ -16,10 +16,6 @@ Using yarn:
|
|
|
16
16
|
yarn add @chriscdn/promise-semaphore
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
## Updating v1 to v2
|
|
20
|
-
|
|
21
|
-
Version 2 adds TypeScript and better inline documentation. The API remains the same, and doesn't introduce any breaking changes.
|
|
22
|
-
|
|
23
19
|
## API
|
|
24
20
|
|
|
25
21
|
### Create an instance
|
|
@@ -29,7 +25,7 @@ import Semaphore from "@chriscdn/promise-semaphore";
|
|
|
29
25
|
const semaphore = new Semaphore([maxConcurrent]);
|
|
30
26
|
```
|
|
31
27
|
|
|
32
|
-
The `maxConcurrent` parameter is optional
|
|
28
|
+
The `maxConcurrent` parameter is optional and defaults to `1` (making it an exclusive lock or _binary semaphore_). An integer greater than `1` can be used to allow multiple concurrent executions from separate iterations of the event loop.
|
|
33
29
|
|
|
34
30
|
### Acquire a lock
|
|
35
31
|
|
|
@@ -37,7 +33,7 @@ The `maxConcurrent` parameter is optional, and defaults to `1` (making it an exc
|
|
|
37
33
|
semaphore.acquire([key]);
|
|
38
34
|
```
|
|
39
35
|
|
|
40
|
-
This returns a `Promise
|
|
36
|
+
This returns a `Promise` that resolves once a lock is acquired. The `key` parameter is optional and allows the same `Semaphore` instance to manage locks in different contexts. Additional details are provided in the second example.
|
|
41
37
|
|
|
42
38
|
### Release a lock
|
|
43
39
|
|
|
@@ -45,7 +41,7 @@ This returns a `Promise`, which resolves once a lock has been acquired. The `key
|
|
|
45
41
|
semaphore.release([key]);
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
The `release`
|
|
44
|
+
The `release` method should be called within a `finally` block (whether using promises or a `try/catch` block) to ensure the lock is released.
|
|
49
45
|
|
|
50
46
|
### Check if a lock can be acquired
|
|
51
47
|
|
|
@@ -53,15 +49,31 @@ The `release` call should be executed from a `finally` block (whether using prom
|
|
|
53
49
|
semaphore.canAcquire([key]);
|
|
54
50
|
```
|
|
55
51
|
|
|
56
|
-
This
|
|
52
|
+
This synchronous method returns `true` if a lock can be immediately acquired, and `false` otherwise.
|
|
53
|
+
|
|
54
|
+
### `count`
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
semaphore.count([key]);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This synchronous function returns the current number of locks.
|
|
61
|
+
|
|
62
|
+
### `wait` method
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
semaphore.wait([key]);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This asynchronous function resolves immediately if `count` is 0; otherwise, it resolves once `count` reaches 0.
|
|
57
69
|
|
|
58
|
-
### request
|
|
70
|
+
### `request` method
|
|
59
71
|
|
|
60
72
|
```js
|
|
61
|
-
const results = await semaphore.request(fn [,key])
|
|
73
|
+
const results = await semaphore.request(fn [, key]);
|
|
62
74
|
```
|
|
63
75
|
|
|
64
|
-
This function reduces boilerplate when using `acquire` and `release`. It returns a promise
|
|
76
|
+
This function reduces boilerplate when using `acquire` and `release`. It returns a promise that resolves when `fn` completes. It is functionally equivalent to:
|
|
65
77
|
|
|
66
78
|
```js
|
|
67
79
|
try {
|
|
@@ -72,23 +84,21 @@ try {
|
|
|
72
84
|
}
|
|
73
85
|
```
|
|
74
86
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### requestIfAvailable function
|
|
87
|
+
### `requestIfAvailable` method
|
|
78
88
|
|
|
79
89
|
```js
|
|
80
|
-
const results = await semaphore.requestIfAvailable(fn [,key])
|
|
90
|
+
const results = await semaphore.requestIfAvailable(fn [, key]);
|
|
81
91
|
```
|
|
82
92
|
|
|
83
93
|
This is functionally equivalent to:
|
|
84
94
|
|
|
85
95
|
```js
|
|
86
|
-
const results = semaphore.canAcquire([key]
|
|
87
|
-
await semaphore.request(fn, [key])
|
|
88
|
-
null
|
|
96
|
+
const results = semaphore.canAcquire([key])
|
|
97
|
+
? await semaphore.request(fn, [key])
|
|
98
|
+
: null;
|
|
89
99
|
```
|
|
90
100
|
|
|
91
|
-
This is useful in
|
|
101
|
+
This is useful in scenarios where only one instance of a function block should run while discarding additional attempts. For example, handling repeated button clicks.
|
|
92
102
|
|
|
93
103
|
## Example 1
|
|
94
104
|
|
|
@@ -96,66 +106,66 @@ This is useful in situations when only one instance of a function block should r
|
|
|
96
106
|
import Semaphore from "@chriscdn/promise-semaphore";
|
|
97
107
|
const semaphore = new Semaphore();
|
|
98
108
|
|
|
99
|
-
//
|
|
109
|
+
// Using promises
|
|
100
110
|
semaphore
|
|
101
111
|
.acquire()
|
|
102
112
|
.then(() => {
|
|
103
|
-
// This block executes once a lock is acquired.
|
|
104
|
-
//
|
|
113
|
+
// This block executes once a lock is acquired.
|
|
114
|
+
// If already locked, it waits and executes after all preceding locks are released.
|
|
105
115
|
//
|
|
106
|
-
//
|
|
116
|
+
// Critical operations are performed here.
|
|
107
117
|
})
|
|
108
118
|
.finally(() => {
|
|
109
|
-
//
|
|
119
|
+
// The lock is released, allowing the next queued block to proceed.
|
|
110
120
|
semaphore.release();
|
|
111
121
|
});
|
|
112
122
|
|
|
113
|
-
//
|
|
123
|
+
// Using async/await
|
|
114
124
|
try {
|
|
115
125
|
await semaphore.acquire();
|
|
116
126
|
|
|
117
|
-
//
|
|
127
|
+
// Critical operations are performed here.
|
|
118
128
|
} finally {
|
|
119
129
|
semaphore.release();
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
//
|
|
123
|
-
semaphore.request(() => {
|
|
124
|
-
//
|
|
132
|
+
// Using the request function
|
|
133
|
+
await semaphore.request(() => {
|
|
134
|
+
// Critical operations are performed here.
|
|
125
135
|
});
|
|
126
136
|
```
|
|
127
137
|
|
|
128
138
|
## Example 2
|
|
129
139
|
|
|
130
|
-
|
|
140
|
+
Consider an asynchronous function that downloads a file and saves it to disk:
|
|
131
141
|
|
|
132
142
|
```js
|
|
133
|
-
async
|
|
143
|
+
const downloadAndSave = async (url) => {
|
|
134
144
|
const filePath = urlToFilePath(url);
|
|
135
145
|
|
|
136
146
|
if (await pathExists(filePath)) {
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
await downloadAndSaveToFilepath(url, filePath);
|
|
147
|
+
// The file is already on disk, so no action is required.
|
|
148
|
+
return filePath;
|
|
140
149
|
}
|
|
141
150
|
|
|
151
|
+
await downloadAndSaveToFilepath(url, filePath);
|
|
142
152
|
return filePath;
|
|
143
|
-
}
|
|
153
|
+
};
|
|
144
154
|
```
|
|
145
155
|
|
|
146
|
-
This works
|
|
156
|
+
This approach works as expected until `downloadAndSave()` is called multiple times with the same `url` in quick succession. Without control, it could initiate simultaneous downloads that attempt to write to the same file at the same time.
|
|
147
157
|
|
|
148
|
-
This can be resolved
|
|
158
|
+
This issue can be resolved by using a `Semaphore` with the `key` parameter:
|
|
149
159
|
|
|
150
160
|
```js
|
|
151
161
|
import Semaphore from "@chriscdn/promise-semaphore";
|
|
152
162
|
const semaphore = new Semaphore();
|
|
153
163
|
|
|
154
|
-
async
|
|
164
|
+
const downloadAndSave = async (url) => {
|
|
155
165
|
try {
|
|
156
166
|
await semaphore.acquire(url);
|
|
157
167
|
|
|
158
|
-
// This block continues once a lock on url is acquired.
|
|
168
|
+
// This block continues once a lock on url is acquired. This
|
|
159
169
|
// permits multiple simulataneous downloads for different urls.
|
|
160
170
|
|
|
161
171
|
const filePath = urlToFilePath(url);
|
|
@@ -170,27 +180,26 @@ async function downloadAndSave(url) {
|
|
|
170
180
|
} finally {
|
|
171
181
|
semaphore.release(url);
|
|
172
182
|
}
|
|
173
|
-
}
|
|
183
|
+
};
|
|
174
184
|
```
|
|
175
185
|
|
|
176
|
-
|
|
186
|
+
The same outcome can be achieved using the `request` function:
|
|
177
187
|
|
|
178
188
|
```js
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const filePath = urlToFilePath(url)
|
|
189
|
+
const downloadAndSave = (url) => {
|
|
190
|
+
return semaphore.request(async () => {
|
|
191
|
+
const filePath = urlToFilePath(url);
|
|
183
192
|
|
|
184
193
|
if (await pathExists(filePath)) {
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
await downloadAndSaveToFilepath(url, filePath)
|
|
194
|
+
// The file is already on disk, so no action is required.
|
|
195
|
+
return filePath;
|
|
188
196
|
}
|
|
189
197
|
|
|
190
|
-
|
|
191
|
-
}, url)
|
|
198
|
+
await downloadAndSaveToFilepath(url, filePath);
|
|
192
199
|
|
|
193
|
-
|
|
200
|
+
return filePath;
|
|
201
|
+
}, url);
|
|
202
|
+
};
|
|
194
203
|
```
|
|
195
204
|
|
|
196
205
|
## License
|
|
@@ -1,143 +1,170 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
1
3
|
import Semaphore from "../src/index";
|
|
2
4
|
|
|
3
5
|
const pause = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
describe("All test", () => {
|
|
8
|
+
it("Acquire & Release Basic", async () => {
|
|
9
|
+
const semaphore = new Semaphore();
|
|
10
|
+
expect(semaphore.canAcquire()).toBe(true);
|
|
11
|
+
await semaphore.acquire();
|
|
12
|
+
expect(semaphore.canAcquire()).toBe(false);
|
|
13
|
+
expect(semaphore.hasTasks()).toBe(true);
|
|
14
|
+
expect(semaphore.count()).toBe(1);
|
|
15
|
+
semaphore.release();
|
|
16
|
+
expect(semaphore.canAcquire()).toBe(true);
|
|
17
|
+
expect(semaphore.count()).toBe(0);
|
|
18
|
+
});
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
it("Semaphore 1", async () => {
|
|
21
|
+
const semaphore = new Semaphore();
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
semaphore
|
|
24
|
+
.acquire()
|
|
25
|
+
.then(async () => await pause(1000))
|
|
26
|
+
.finally(() => semaphore.release());
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
expect(semaphore.canAcquire()).toBe(false);
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
await pause(600);
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
expect(semaphore.canAcquire()).toBe(false);
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
await pause(600);
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
});
|
|
36
|
+
expect(semaphore.canAcquire()).toBe(true);
|
|
37
|
+
});
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
it("Semaphore 2", async () => {
|
|
40
|
+
const semaphore = new Semaphore();
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
let tester = 0;
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
semaphore
|
|
45
|
+
.acquire()
|
|
46
|
+
.then(() => pause(1000))
|
|
47
|
+
.then(() => (tester = 10))
|
|
48
|
+
.finally(() => semaphore.release());
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
50
|
+
// tests acquire waits for previous to complete
|
|
51
|
+
await semaphore
|
|
52
|
+
.acquire()
|
|
53
|
+
.then(() => expect(tester).toBe(10))
|
|
54
|
+
.finally(() => semaphore.release());
|
|
55
|
+
});
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
it("Semaphore 3", async () => {
|
|
58
|
+
const semaphore = new Semaphore(2);
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
let tester = 0;
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
semaphore
|
|
63
|
+
.acquire()
|
|
64
|
+
.then(() => pause(1000))
|
|
65
|
+
.then(() => (tester = 20))
|
|
66
|
+
.finally(() => semaphore.release());
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
semaphore
|
|
69
|
+
.acquire()
|
|
70
|
+
.then(() => pause(500))
|
|
71
|
+
.then(() => (tester = 10))
|
|
72
|
+
.finally(() => semaphore.release());
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
expect(semaphore.count()).toBe(2);
|
|
75
|
+
expect(semaphore.hasTasks()).toBe(true);
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
// expect 10 since this next block will run before the first has completed
|
|
78
|
+
await semaphore
|
|
79
|
+
.acquire()
|
|
80
|
+
.then(() => expect(tester).toBe(10))
|
|
81
|
+
.finally(() => semaphore.release());
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
83
|
+
expect(semaphore.count()).toBe(1);
|
|
84
|
+
expect(semaphore.hasTasks()).toBe(true);
|
|
85
|
+
expect(semaphore.canAcquire()).toBe(true);
|
|
86
|
+
});
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
it("Request 1", async () => {
|
|
89
|
+
const semaphore = new Semaphore();
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
let tester = 0;
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
semaphore.request(async () => {
|
|
94
|
+
await pause(1000);
|
|
95
|
+
tester = 20;
|
|
96
|
+
});
|
|
94
97
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
semaphore.request(async () => {
|
|
99
|
+
await pause(500);
|
|
100
|
+
tester = 10;
|
|
101
|
+
});
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
await semaphore.request(async () => {
|
|
104
|
+
expect(tester).toBe(10);
|
|
105
|
+
});
|
|
102
106
|
});
|
|
103
|
-
});
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
it("Request 2", async () => {
|
|
109
|
+
const semaphore = new Semaphore(2);
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
let tester = 0;
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
semaphore.request(async () => {
|
|
114
|
+
await pause(1000);
|
|
115
|
+
tester = 20;
|
|
116
|
+
});
|
|
114
117
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
semaphore.request(async () => {
|
|
119
|
+
await pause(500);
|
|
120
|
+
tester = 10;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await semaphore.request(async () => {
|
|
124
|
+
expect(tester).toBe(10);
|
|
125
|
+
});
|
|
118
126
|
});
|
|
119
127
|
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
it("Request 3", async () => {
|
|
129
|
+
const semaphore = new Semaphore(3);
|
|
130
|
+
|
|
131
|
+
let tester = 0;
|
|
132
|
+
|
|
133
|
+
semaphore.request(async () => {
|
|
134
|
+
await pause(1000);
|
|
135
|
+
tester = 20;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
semaphore.request(async () => {
|
|
139
|
+
await pause(500);
|
|
140
|
+
tester = 10;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await semaphore.request(async () => {
|
|
144
|
+
expect(tester).toBe(0);
|
|
145
|
+
});
|
|
122
146
|
});
|
|
123
147
|
});
|
|
124
148
|
|
|
125
|
-
|
|
126
|
-
const semaphore = new Semaphore(
|
|
149
|
+
describe("Wait TEST", () => {
|
|
150
|
+
const semaphore = new Semaphore(2);
|
|
127
151
|
|
|
128
|
-
|
|
152
|
+
it("Acquire & Release Basic", async () => {
|
|
153
|
+
let tester = 0;
|
|
129
154
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
155
|
+
semaphore
|
|
156
|
+
.acquire()
|
|
157
|
+
.then(() => pause(1000))
|
|
158
|
+
.then(() => (tester = 20))
|
|
159
|
+
.finally(() => semaphore.release());
|
|
134
160
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
semaphore
|
|
162
|
+
.acquire()
|
|
163
|
+
.then(() => pause(500))
|
|
164
|
+
.then(() => (tester = 10))
|
|
165
|
+
.finally(() => semaphore.release());
|
|
139
166
|
|
|
140
|
-
|
|
141
|
-
expect(tester).toBe(
|
|
167
|
+
await semaphore.wait(); // WAIT for all tasks to finish
|
|
168
|
+
expect(tester).toBe(20);
|
|
142
169
|
});
|
|
143
170
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -2,14 +2,12 @@ declare class Semaphore {
|
|
|
2
2
|
private semaphoreInstances;
|
|
3
3
|
private maxConcurrent;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
5
|
* @param {number} [maxConcurrent] The maximum number of concurrent locks.
|
|
7
6
|
*/
|
|
8
7
|
constructor(maxConcurrent?: number);
|
|
9
8
|
private hasSemaphoreInstance;
|
|
10
9
|
private getSemaphoreInstance;
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
13
11
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
14
12
|
*/
|
|
15
13
|
private tidy;
|
|
@@ -22,12 +20,10 @@ declare class Semaphore {
|
|
|
22
20
|
*/
|
|
23
21
|
canAcquire(key?: string | number): boolean;
|
|
24
22
|
/**
|
|
25
|
-
*
|
|
26
23
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
27
24
|
*/
|
|
28
25
|
acquire(key?: string | number): Promise<void>;
|
|
29
26
|
/**
|
|
30
|
-
*
|
|
31
27
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
32
28
|
*/
|
|
33
29
|
release(key?: string | number): void;
|
|
@@ -38,13 +34,11 @@ declare class Semaphore {
|
|
|
38
34
|
*/
|
|
39
35
|
count(key?: string | number): number;
|
|
40
36
|
/**
|
|
41
|
-
*
|
|
42
37
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
43
38
|
* @returns {boolean} True if the semaphore and key has locks, false otherwise.
|
|
44
39
|
*/
|
|
45
40
|
hasTasks(key?: string | number): boolean;
|
|
46
41
|
/**
|
|
47
|
-
*
|
|
48
42
|
* @param {Function<T>} fn The function to execute.
|
|
49
43
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
50
44
|
* @returns {Promise<T>}
|
|
@@ -59,5 +53,6 @@ declare class Semaphore {
|
|
|
59
53
|
* @returns {Promise<T>}
|
|
60
54
|
*/
|
|
61
55
|
requestIfAvailable<T>(fn: Function, key?: string | number): Promise<T | null>;
|
|
56
|
+
wait(key?: string | number): Promise<void>;
|
|
62
57
|
}
|
|
63
58
|
export default Semaphore;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){var t=function(e
|
|
1
|
+
function e(e){var t=function(e){if("object"!=typeof e||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:t+""}var t=/*#__PURE__*/function(){function t(e){this.queue=void 0,this.waitQueue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.waitQueue=[],this.maxConcurrent=e,this.count=0}var n,r,i=t.prototype;return i.incrementCount=function(){this.count++},i.decrementCount=function(){this.count--,0===this.count&&(this.waitQueue.forEach(function(e){return e()}),this.waitQueue=[])},i.acquire=function(){var e=this;return this.canAcquire?(this.incrementCount(),Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},i.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.decrementCount()},i.wait=function(){var e=this;return new Promise(function(t){return e.waitQueue.push(t)})},n=t,(r=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(t,n){for(var r=0;r<n.length;r++){var i=n[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,e(i.key),i)}}(n.prototype,r),Object.defineProperty(n,"prototype",{writable:!1}),n}(),n="_default";module.exports=/*#__PURE__*/function(){function e(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=e.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=n),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(e){return void 0===e&&(e=n),this.hasSemaphoreInstance(e)||(this.semaphoreInstances[e]=new t(this.maxConcurrent)),this.semaphoreInstances[e]},r.tidy=function(e){void 0===e&&(e=n),this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]},r.canAcquire=function(e){return void 0===e&&(e=n),this.getSemaphoreInstance(e).canAcquire},r.acquire=function(e){return void 0===e&&(e=n),this.getSemaphoreInstance(e).acquire()},r.release=function(e){void 0===e&&(e=n),this.getSemaphoreInstance(e).release(),this.tidy(e)},r.count=function(e){return void 0===e&&(e=n),this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0},r.hasTasks=function(e){return void 0===e&&(e=n),this.count(e)>0},r.request=function(e,t){void 0===t&&(t=n);try{var r=this;return Promise.resolve(function(n,i){try{var o=Promise.resolve(r.acquire(t)).then(function(){return Promise.resolve(e())})}catch(e){return i(!0,e)}return o&&o.then?o.then(i.bind(null,!1),i.bind(null,!0)):i(!1,o)}(0,function(e,n){if(r.release(t),e)throw n;return n}))}catch(e){return Promise.reject(e)}},r.requestIfAvailable=function(e,t){void 0===t&&(t=n);try{return this.canAcquire(t)?Promise.resolve(this.request(e,t)):Promise.resolve(null)}catch(e){return Promise.reject(e)}},r.wait=function(e){void 0===e&&(e=n);try{return this.hasTasks(e)?Promise.resolve(this.getSemaphoreInstance(e).wait()):Promise.resolve()}catch(e){return Promise.reject(e)}},e}();
|
|
2
2
|
//# sourceMappingURL=promise-semaphore.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-semaphore.cjs","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private maxConcurrent: number;\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.
|
|
1
|
+
{"version":3,"file":"promise-semaphore.cjs","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private waitQueue: Array<Function>;\n private maxConcurrent: number;\n\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.waitQueue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n private incrementCount() {\n this.count++;\n }\n\n private decrementCount() {\n this.count--;\n\n if (this.count === 0) {\n this.waitQueue.forEach((resolve) => resolve());\n this.waitQueue = [];\n }\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.incrementCount();\n return Promise.resolve();\n } else {\n return new Promise((resolve) => this.queue.push(resolve));\n }\n }\n\n release(): void {\n const resolveFunc = this.queue.shift();\n\n if (resolveFunc) {\n // Give the micro task queue a small break instead of calling resolveFunc() directly\n setTimeout(resolveFunc, 0);\n } else {\n this.decrementCount();\n }\n }\n\n wait(): Promise<void> {\n return new Promise((resolve) => this.waitQueue.push(resolve));\n }\n}\n\nconst defaultKey = \"_default\";\n\nclass Semaphore {\n private semaphoreInstances: Record<string | number, SemaphoreItem>;\n private maxConcurrent: number;\n\n /**\n * @param {number} [maxConcurrent] The maximum number of concurrent locks.\n */\n constructor(maxConcurrent: number = 1) {\n this.semaphoreInstances = {};\n this.maxConcurrent = maxConcurrent;\n }\n\n private hasSemaphoreInstance(key: string | number = defaultKey) {\n return Boolean(this.semaphoreInstances[key]);\n }\n\n private getSemaphoreInstance(key: string | number = defaultKey) {\n if (!this.hasSemaphoreInstance(key)) {\n this.semaphoreInstances[key] = new SemaphoreItem(this.maxConcurrent);\n }\n return this.semaphoreInstances[key];\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n private tidy(key: string | number = defaultKey): void {\n if (\n this.hasSemaphoreInstance(key) &&\n this.getSemaphoreInstance(key).count === 0\n ) {\n delete this.semaphoreInstances[key];\n }\n }\n\n /**\n * A synchronous function to determine whether a lock can be acquired.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} Returns true if the lock on `key` can be acquired, false\n * otherwise.\n */\n canAcquire(key: string | number = defaultKey): boolean {\n return this.getSemaphoreInstance(key).canAcquire;\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n acquire(key: string | number = defaultKey) {\n return this.getSemaphoreInstance(key).acquire();\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n release(key: string | number = defaultKey): void {\n this.getSemaphoreInstance(key).release();\n this.tidy(key);\n }\n\n /**\n * The number of active locks. Will always be less or equal to `max`.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n count(key: string | number = defaultKey): number {\n if (this.hasSemaphoreInstance(key)) {\n return this.getSemaphoreInstance(key).count;\n } else {\n return 0;\n }\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} True if the semaphore and key has locks, false otherwise.\n */\n hasTasks(key: string | number = defaultKey): boolean {\n return this.count(key) > 0;\n }\n\n /**\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async request<T>(\n fn: Function,\n key: string | number = defaultKey,\n ): Promise<T> {\n try {\n await this.acquire(key);\n return await fn();\n } finally {\n this.release(key);\n }\n }\n\n /**\n * Asynchronously executes `fn` if a lock can be immediately acquired.\n * Otherwise, returns null.\n *\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async requestIfAvailable<T>(\n fn: Function,\n key: string | number = defaultKey,\n ): Promise<T | null> {\n if (this.canAcquire(key)) {\n return this.request(fn, key);\n } else {\n return null;\n }\n }\n\n async wait(key: string | number = defaultKey) {\n if (this.hasTasks(key)) {\n return this.getSemaphoreInstance(key).wait();\n } else {\n return Promise.resolve();\n }\n }\n}\n\nexport default Semaphore;\n"],"names":["SemaphoreItem","maxConcurrent","queue","waitQueue","count","this","_proto","prototype","incrementCount","decrementCount","forEach","resolve","acquire","_this","canAcquire","Promise","push","release","resolveFunc","shift","setTimeout","wait","_this2","key","get","defaultKey","Semaphore","semaphoreInstances","_proto2","hasSemaphoreInstance","Boolean","getSemaphoreInstance","tidy","hasTasks","request","fn","_this3","then","_finallyRethrows","_wasThrown","_result","e","reject","requestIfAvailable"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
class e{constructor(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}get canAcquire(){return this.count<this.maxConcurrent}acquire(){return this.canAcquire?(this.
|
|
1
|
+
class e{constructor(e){this.queue=void 0,this.waitQueue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.waitQueue=[],this.maxConcurrent=e,this.count=0}get canAcquire(){return this.count<this.maxConcurrent}incrementCount(){this.count++}decrementCount(){this.count--,0===this.count&&(this.waitQueue.forEach(e=>e()),this.waitQueue=[])}acquire(){return this.canAcquire?(this.incrementCount(),Promise.resolve()):new Promise(e=>this.queue.push(e))}release(){const e=this.queue.shift();e?setTimeout(e,0):this.decrementCount()}wait(){return new Promise(e=>this.waitQueue.push(e))}}const t="_default";class s{constructor(e=1){this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}hasSemaphoreInstance(e=t){return Boolean(this.semaphoreInstances[e])}getSemaphoreInstance(s=t){return this.hasSemaphoreInstance(s)||(this.semaphoreInstances[s]=new e(this.maxConcurrent)),this.semaphoreInstances[s]}tidy(e=t){this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]}canAcquire(e=t){return this.getSemaphoreInstance(e).canAcquire}acquire(e=t){return this.getSemaphoreInstance(e).acquire()}release(e=t){this.getSemaphoreInstance(e).release(),this.tidy(e)}count(e=t){return this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0}hasTasks(e=t){return this.count(e)>0}async request(e,s=t){try{return await this.acquire(s),await e()}finally{this.release(s)}}async requestIfAvailable(e,s=t){return this.canAcquire(s)?this.request(e,s):null}async wait(e=t){return this.hasTasks(e)?this.getSemaphoreInstance(e).wait():Promise.resolve()}}export{s as default};
|
|
2
2
|
//# sourceMappingURL=promise-semaphore.modern.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-semaphore.modern.js","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private maxConcurrent: number;\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.
|
|
1
|
+
{"version":3,"file":"promise-semaphore.modern.js","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private waitQueue: Array<Function>;\n private maxConcurrent: number;\n\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.waitQueue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n private incrementCount() {\n this.count++;\n }\n\n private decrementCount() {\n this.count--;\n\n if (this.count === 0) {\n this.waitQueue.forEach((resolve) => resolve());\n this.waitQueue = [];\n }\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.incrementCount();\n return Promise.resolve();\n } else {\n return new Promise((resolve) => this.queue.push(resolve));\n }\n }\n\n release(): void {\n const resolveFunc = this.queue.shift();\n\n if (resolveFunc) {\n // Give the micro task queue a small break instead of calling resolveFunc() directly\n setTimeout(resolveFunc, 0);\n } else {\n this.decrementCount();\n }\n }\n\n wait(): Promise<void> {\n return new Promise((resolve) => this.waitQueue.push(resolve));\n }\n}\n\nconst defaultKey = \"_default\";\n\nclass Semaphore {\n private semaphoreInstances: Record<string | number, SemaphoreItem>;\n private maxConcurrent: number;\n\n /**\n * @param {number} [maxConcurrent] The maximum number of concurrent locks.\n */\n constructor(maxConcurrent: number = 1) {\n this.semaphoreInstances = {};\n this.maxConcurrent = maxConcurrent;\n }\n\n private hasSemaphoreInstance(key: string | number = defaultKey) {\n return Boolean(this.semaphoreInstances[key]);\n }\n\n private getSemaphoreInstance(key: string | number = defaultKey) {\n if (!this.hasSemaphoreInstance(key)) {\n this.semaphoreInstances[key] = new SemaphoreItem(this.maxConcurrent);\n }\n return this.semaphoreInstances[key];\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n private tidy(key: string | number = defaultKey): void {\n if (\n this.hasSemaphoreInstance(key) &&\n this.getSemaphoreInstance(key).count === 0\n ) {\n delete this.semaphoreInstances[key];\n }\n }\n\n /**\n * A synchronous function to determine whether a lock can be acquired.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} Returns true if the lock on `key` can be acquired, false\n * otherwise.\n */\n canAcquire(key: string | number = defaultKey): boolean {\n return this.getSemaphoreInstance(key).canAcquire;\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n acquire(key: string | number = defaultKey) {\n return this.getSemaphoreInstance(key).acquire();\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n release(key: string | number = defaultKey): void {\n this.getSemaphoreInstance(key).release();\n this.tidy(key);\n }\n\n /**\n * The number of active locks. Will always be less or equal to `max`.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n count(key: string | number = defaultKey): number {\n if (this.hasSemaphoreInstance(key)) {\n return this.getSemaphoreInstance(key).count;\n } else {\n return 0;\n }\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} True if the semaphore and key has locks, false otherwise.\n */\n hasTasks(key: string | number = defaultKey): boolean {\n return this.count(key) > 0;\n }\n\n /**\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async request<T>(\n fn: Function,\n key: string | number = defaultKey,\n ): Promise<T> {\n try {\n await this.acquire(key);\n return await fn();\n } finally {\n this.release(key);\n }\n }\n\n /**\n * Asynchronously executes `fn` if a lock can be immediately acquired.\n * Otherwise, returns null.\n *\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async requestIfAvailable<T>(\n fn: Function,\n key: string | number = defaultKey,\n ): Promise<T | null> {\n if (this.canAcquire(key)) {\n return this.request(fn, key);\n } else {\n return null;\n }\n }\n\n async wait(key: string | number = defaultKey) {\n if (this.hasTasks(key)) {\n return this.getSemaphoreInstance(key).wait();\n } else {\n return Promise.resolve();\n }\n }\n}\n\nexport default Semaphore;\n"],"names":["SemaphoreItem","constructor","maxConcurrent","this","queue","waitQueue","count","canAcquire","incrementCount","decrementCount","forEach","resolve","acquire","Promise","push","release","resolveFunc","shift","setTimeout","wait","defaultKey","Semaphore","semaphoreInstances","hasSemaphoreInstance","key","Boolean","getSemaphoreInstance","tidy","hasTasks","request","fn","requestIfAvailable"],"mappings":"AAAA,MAAMA,EAUJC,WAAAA,CAAYC,GAAqBC,KATzBC,WAAK,EAAAD,KACLE,eAAS,EAAAF,KACTD,mBAKDI,EAAAA,KAAAA,WAGL,EAAAH,KAAKC,MAAQ,GACbD,KAAKE,UAAY,GACjBF,KAAKD,cAAgBA,EACrBC,KAAKG,MAAQ,CACf,CAEA,cAAIC,GACF,YAAYD,MAAQH,KAAKD,aAC3B,CAEQM,cAAAA,GACNL,KAAKG,OACP,CAEQG,cAAAA,GACNN,KAAKG,QAEc,IAAfH,KAAKG,QACPH,KAAKE,UAAUK,QAASC,GAAYA,KACpCR,KAAKE,UAAY,GAErB,CAEAO,OAAAA,GACE,OAAIT,KAAKI,YACPJ,KAAKK,iBACEK,QAAQF,WAEJ,IAAAE,QAASF,GAAYR,KAAKC,MAAMU,KAAKH,GAEpD,CAEAI,OAAAA,GACE,MAAMC,EAAcb,KAAKC,MAAMa,QAE3BD,EAEFE,WAAWF,EAAa,GAExBb,KAAKM,gBAET,CAEAU,IAAAA,GACE,OAAW,IAAAN,QAASF,GAAYR,KAAKE,UAAUS,KAAKH,GACtD,EAGF,MAAMS,EAAa,WAEnB,MAAMC,EAOJpB,WAAAA,CAAYC,EAAwB,GAN5BoB,KAAAA,wBACApB,EAAAA,KAAAA,qBAMNC,KAAKmB,mBAAqB,GAC1BnB,KAAKD,cAAgBA,CACvB,CAEQqB,oBAAAA,CAAqBC,EAAuBJ,GAClD,OAAOK,QAAQtB,KAAKmB,mBAAmBE,GACzC,CAEQE,oBAAAA,CAAqBF,EAAuBJ,GAIlD,OAHKjB,KAAKoB,qBAAqBC,KAC7BrB,KAAKmB,mBAAmBE,GAAO,IAAIxB,EAAcG,KAAKD,gBAE7CC,KAACmB,mBAAmBE,EACjC,CAKQG,IAAAA,CAAKH,EAAuBJ,GAEhCjB,KAAKoB,qBAAqBC,IACe,IAAzCrB,KAAKuB,qBAAqBF,GAAKlB,cAEpBH,KAACmB,mBAAmBE,EAEnC,CASAjB,UAAAA,CAAWiB,EAAuBJ,GAChC,OAAWjB,KAACuB,qBAAqBF,GAAKjB,UACxC,CAKAK,OAAAA,CAAQY,EAAuBJ,GAC7B,OAAWjB,KAACuB,qBAAqBF,GAAKZ,SACxC,CAKAG,OAAAA,CAAQS,EAAuBJ,GAC7BjB,KAAKuB,qBAAqBF,GAAKT,UAC/BZ,KAAKwB,KAAKH,EACZ,CAOAlB,KAAAA,CAAMkB,EAAuBJ,GAC3B,OAAIjB,KAAKoB,qBAAqBC,GACrBrB,KAAKuB,qBAAqBF,GAAKlB,MAGvC,CACH,CAMAsB,QAAAA,CAASJ,EAAuBJ,GAC9B,OAAOjB,KAAKG,MAAMkB,GAAO,CAC3B,CAOA,aAAMK,CACJC,EACAN,EAAuBJ,GAEvB,IAEE,aADMjB,KAAKS,QAAQY,SACNM,GACd,CAAA,QACC3B,KAAKY,QAAQS,EACd,CACH,CAUA,wBAAMO,CACJD,EACAN,EAAuBJ,GAEvB,OAAIjB,KAAKI,WAAWiB,GACPrB,KAAC0B,QAAQC,EAAIN,GAEjB,IAEX,CAEA,UAAML,CAAKK,EAAuBJ,GAChC,OAAIjB,KAAKyB,SAASJ,GACLrB,KAACuB,qBAAqBF,GAAKL,OAE/BN,QAAQF,SAEnB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){var t=function(e
|
|
1
|
+
function e(e){var t=function(e){if("object"!=typeof e||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:t+""}var t=/*#__PURE__*/function(){function t(e){this.queue=void 0,this.waitQueue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.waitQueue=[],this.maxConcurrent=e,this.count=0}var n,r,i=t.prototype;return i.incrementCount=function(){this.count++},i.decrementCount=function(){this.count--,0===this.count&&(this.waitQueue.forEach(function(e){return e()}),this.waitQueue=[])},i.acquire=function(){var e=this;return this.canAcquire?(this.incrementCount(),Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},i.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.decrementCount()},i.wait=function(){var e=this;return new Promise(function(t){return e.waitQueue.push(t)})},n=t,(r=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(t,n){for(var r=0;r<n.length;r++){var i=n[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,e(i.key),i)}}(n.prototype,r),Object.defineProperty(n,"prototype",{writable:!1}),n}(),n="_default",r=/*#__PURE__*/function(){function e(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=e.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=n),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(e){return void 0===e&&(e=n),this.hasSemaphoreInstance(e)||(this.semaphoreInstances[e]=new t(this.maxConcurrent)),this.semaphoreInstances[e]},r.tidy=function(e){void 0===e&&(e=n),this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]},r.canAcquire=function(e){return void 0===e&&(e=n),this.getSemaphoreInstance(e).canAcquire},r.acquire=function(e){return void 0===e&&(e=n),this.getSemaphoreInstance(e).acquire()},r.release=function(e){void 0===e&&(e=n),this.getSemaphoreInstance(e).release(),this.tidy(e)},r.count=function(e){return void 0===e&&(e=n),this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0},r.hasTasks=function(e){return void 0===e&&(e=n),this.count(e)>0},r.request=function(e,t){void 0===t&&(t=n);try{var r=this;return Promise.resolve(function(n,i){try{var o=Promise.resolve(r.acquire(t)).then(function(){return Promise.resolve(e())})}catch(e){return i(!0,e)}return o&&o.then?o.then(i.bind(null,!1),i.bind(null,!0)):i(!1,o)}(0,function(e,n){if(r.release(t),e)throw n;return n}))}catch(e){return Promise.reject(e)}},r.requestIfAvailable=function(e,t){void 0===t&&(t=n);try{return this.canAcquire(t)?Promise.resolve(this.request(e,t)):Promise.resolve(null)}catch(e){return Promise.reject(e)}},r.wait=function(e){void 0===e&&(e=n);try{return this.hasTasks(e)?Promise.resolve(this.getSemaphoreInstance(e).wait()):Promise.resolve()}catch(e){return Promise.reject(e)}},e}();export{r as default};
|
|
2
2
|
//# sourceMappingURL=promise-semaphore.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-semaphore.module.js","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private maxConcurrent: number;\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.
|
|
1
|
+
{"version":3,"file":"promise-semaphore.module.js","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private waitQueue: Array<Function>;\n private maxConcurrent: number;\n\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.waitQueue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n private incrementCount() {\n this.count++;\n }\n\n private decrementCount() {\n this.count--;\n\n if (this.count === 0) {\n this.waitQueue.forEach((resolve) => resolve());\n this.waitQueue = [];\n }\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.incrementCount();\n return Promise.resolve();\n } else {\n return new Promise((resolve) => this.queue.push(resolve));\n }\n }\n\n release(): void {\n const resolveFunc = this.queue.shift();\n\n if (resolveFunc) {\n // Give the micro task queue a small break instead of calling resolveFunc() directly\n setTimeout(resolveFunc, 0);\n } else {\n this.decrementCount();\n }\n }\n\n wait(): Promise<void> {\n return new Promise((resolve) => this.waitQueue.push(resolve));\n }\n}\n\nconst defaultKey = \"_default\";\n\nclass Semaphore {\n private semaphoreInstances: Record<string | number, SemaphoreItem>;\n private maxConcurrent: number;\n\n /**\n * @param {number} [maxConcurrent] The maximum number of concurrent locks.\n */\n constructor(maxConcurrent: number = 1) {\n this.semaphoreInstances = {};\n this.maxConcurrent = maxConcurrent;\n }\n\n private hasSemaphoreInstance(key: string | number = defaultKey) {\n return Boolean(this.semaphoreInstances[key]);\n }\n\n private getSemaphoreInstance(key: string | number = defaultKey) {\n if (!this.hasSemaphoreInstance(key)) {\n this.semaphoreInstances[key] = new SemaphoreItem(this.maxConcurrent);\n }\n return this.semaphoreInstances[key];\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n private tidy(key: string | number = defaultKey): void {\n if (\n this.hasSemaphoreInstance(key) &&\n this.getSemaphoreInstance(key).count === 0\n ) {\n delete this.semaphoreInstances[key];\n }\n }\n\n /**\n * A synchronous function to determine whether a lock can be acquired.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} Returns true if the lock on `key` can be acquired, false\n * otherwise.\n */\n canAcquire(key: string | number = defaultKey): boolean {\n return this.getSemaphoreInstance(key).canAcquire;\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n acquire(key: string | number = defaultKey) {\n return this.getSemaphoreInstance(key).acquire();\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n release(key: string | number = defaultKey): void {\n this.getSemaphoreInstance(key).release();\n this.tidy(key);\n }\n\n /**\n * The number of active locks. Will always be less or equal to `max`.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n count(key: string | number = defaultKey): number {\n if (this.hasSemaphoreInstance(key)) {\n return this.getSemaphoreInstance(key).count;\n } else {\n return 0;\n }\n }\n\n /**\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} True if the semaphore and key has locks, false otherwise.\n */\n hasTasks(key: string | number = defaultKey): boolean {\n return this.count(key) > 0;\n }\n\n /**\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async request<T>(\n fn: Function,\n key: string | number = defaultKey,\n ): Promise<T> {\n try {\n await this.acquire(key);\n return await fn();\n } finally {\n this.release(key);\n }\n }\n\n /**\n * Asynchronously executes `fn` if a lock can be immediately acquired.\n * Otherwise, returns null.\n *\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async requestIfAvailable<T>(\n fn: Function,\n key: string | number = defaultKey,\n ): Promise<T | null> {\n if (this.canAcquire(key)) {\n return this.request(fn, key);\n } else {\n return null;\n }\n }\n\n async wait(key: string | number = defaultKey) {\n if (this.hasTasks(key)) {\n return this.getSemaphoreInstance(key).wait();\n } else {\n return Promise.resolve();\n }\n }\n}\n\nexport default Semaphore;\n"],"names":["SemaphoreItem","maxConcurrent","queue","waitQueue","count","this","_proto","prototype","incrementCount","decrementCount","forEach","resolve","acquire","_this","canAcquire","Promise","push","release","resolveFunc","shift","setTimeout","wait","_this2","key","get","defaultKey","Semaphore","semaphoreInstances","_proto2","hasSemaphoreInstance","Boolean","getSemaphoreInstance","tidy","hasTasks","request","fn","_this3","then","_finallyRethrows","_wasThrown","_result","e","reject","requestIfAvailable"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chriscdn/promise-semaphore",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11",
|
|
4
4
|
"description": "Limit or throttle the simultaneous execution of asynchronous code in separate iterations of the event loop.",
|
|
5
5
|
"repository": "https://github.com/chriscdn/promise-semaphore",
|
|
6
6
|
"author": "Christopher Meyer <chris@schwiiz.org>",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"source": "./src/index.ts",
|
|
10
10
|
"main": "./lib/promise-semaphore.cjs",
|
|
11
11
|
"module": "./lib/promise-semaphore.module.js",
|
|
12
|
-
"
|
|
12
|
+
"__unpkg": "./lib/promise-semaphore.umd.js",
|
|
13
13
|
"exports": {
|
|
14
14
|
"types": "./lib/index.d.ts",
|
|
15
15
|
"require": "./lib/promise-semaphore.cjs",
|
|
@@ -17,15 +17,13 @@
|
|
|
17
17
|
},
|
|
18
18
|
"types": "./lib/index.d.ts",
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "rm -rf ./lib/ && microbundle",
|
|
20
|
+
"build": "rm -rf ./lib/ && microbundle --format modern,esm,cjs",
|
|
21
21
|
"dev": "microbundle watch",
|
|
22
|
-
"test": "
|
|
22
|
+
"test": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@types/jest": "^29.5.11",
|
|
26
|
-
"jest": "^29.7.0",
|
|
27
25
|
"microbundle": "^0.15.1",
|
|
28
|
-
"
|
|
26
|
+
"vitest": "^3.1.2"
|
|
29
27
|
},
|
|
30
28
|
"keywords": [
|
|
31
29
|
"promise",
|
|
@@ -34,5 +32,6 @@
|
|
|
34
32
|
"mutex",
|
|
35
33
|
"async",
|
|
36
34
|
"throttle"
|
|
37
|
-
]
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {}
|
|
38
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
class SemaphoreItem {
|
|
2
2
|
private queue: Array<Function>;
|
|
3
|
+
private waitQueue: Array<Function>;
|
|
3
4
|
private maxConcurrent: number;
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* The number of locks.
|
|
6
8
|
*/
|
|
@@ -8,6 +10,7 @@ class SemaphoreItem {
|
|
|
8
10
|
|
|
9
11
|
constructor(maxConcurrent: number) {
|
|
10
12
|
this.queue = [];
|
|
13
|
+
this.waitQueue = [];
|
|
11
14
|
this.maxConcurrent = maxConcurrent;
|
|
12
15
|
this.count = 0;
|
|
13
16
|
}
|
|
@@ -16,9 +19,22 @@ class SemaphoreItem {
|
|
|
16
19
|
return this.count < this.maxConcurrent;
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
private incrementCount() {
|
|
23
|
+
this.count++;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private decrementCount() {
|
|
27
|
+
this.count--;
|
|
28
|
+
|
|
29
|
+
if (this.count === 0) {
|
|
30
|
+
this.waitQueue.forEach((resolve) => resolve());
|
|
31
|
+
this.waitQueue = [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
acquire(): Promise<void> {
|
|
20
36
|
if (this.canAcquire) {
|
|
21
|
-
this.
|
|
37
|
+
this.incrementCount();
|
|
22
38
|
return Promise.resolve();
|
|
23
39
|
} else {
|
|
24
40
|
return new Promise((resolve) => this.queue.push(resolve));
|
|
@@ -31,11 +47,14 @@ class SemaphoreItem {
|
|
|
31
47
|
if (resolveFunc) {
|
|
32
48
|
// Give the micro task queue a small break instead of calling resolveFunc() directly
|
|
33
49
|
setTimeout(resolveFunc, 0);
|
|
34
|
-
// resolveFunc()
|
|
35
50
|
} else {
|
|
36
|
-
this.
|
|
51
|
+
this.decrementCount();
|
|
37
52
|
}
|
|
38
53
|
}
|
|
54
|
+
|
|
55
|
+
wait(): Promise<void> {
|
|
56
|
+
return new Promise((resolve) => this.waitQueue.push(resolve));
|
|
57
|
+
}
|
|
39
58
|
}
|
|
40
59
|
|
|
41
60
|
const defaultKey = "_default";
|
|
@@ -45,7 +64,6 @@ class Semaphore {
|
|
|
45
64
|
private maxConcurrent: number;
|
|
46
65
|
|
|
47
66
|
/**
|
|
48
|
-
*
|
|
49
67
|
* @param {number} [maxConcurrent] The maximum number of concurrent locks.
|
|
50
68
|
*/
|
|
51
69
|
constructor(maxConcurrent: number = 1) {
|
|
@@ -65,7 +83,6 @@ class Semaphore {
|
|
|
65
83
|
}
|
|
66
84
|
|
|
67
85
|
/**
|
|
68
|
-
*
|
|
69
86
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
70
87
|
*/
|
|
71
88
|
private tidy(key: string | number = defaultKey): void {
|
|
@@ -89,7 +106,6 @@ class Semaphore {
|
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
/**
|
|
92
|
-
*
|
|
93
109
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
94
110
|
*/
|
|
95
111
|
acquire(key: string | number = defaultKey) {
|
|
@@ -97,7 +113,6 @@ class Semaphore {
|
|
|
97
113
|
}
|
|
98
114
|
|
|
99
115
|
/**
|
|
100
|
-
*
|
|
101
116
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
102
117
|
*/
|
|
103
118
|
release(key: string | number = defaultKey): void {
|
|
@@ -119,7 +134,6 @@ class Semaphore {
|
|
|
119
134
|
}
|
|
120
135
|
|
|
121
136
|
/**
|
|
122
|
-
*
|
|
123
137
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
124
138
|
* @returns {boolean} True if the semaphore and key has locks, false otherwise.
|
|
125
139
|
*/
|
|
@@ -128,14 +142,13 @@ class Semaphore {
|
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
/**
|
|
131
|
-
*
|
|
132
145
|
* @param {Function<T>} fn The function to execute.
|
|
133
146
|
* @param {string | number} [key]- Optional, the semaphore key.
|
|
134
147
|
* @returns {Promise<T>}
|
|
135
148
|
*/
|
|
136
149
|
async request<T>(
|
|
137
150
|
fn: Function,
|
|
138
|
-
key: string | number = defaultKey
|
|
151
|
+
key: string | number = defaultKey,
|
|
139
152
|
): Promise<T> {
|
|
140
153
|
try {
|
|
141
154
|
await this.acquire(key);
|
|
@@ -155,7 +168,7 @@ class Semaphore {
|
|
|
155
168
|
*/
|
|
156
169
|
async requestIfAvailable<T>(
|
|
157
170
|
fn: Function,
|
|
158
|
-
key: string | number = defaultKey
|
|
171
|
+
key: string | number = defaultKey,
|
|
159
172
|
): Promise<T | null> {
|
|
160
173
|
if (this.canAcquire(key)) {
|
|
161
174
|
return this.request(fn, key);
|
|
@@ -163,6 +176,14 @@ class Semaphore {
|
|
|
163
176
|
return null;
|
|
164
177
|
}
|
|
165
178
|
}
|
|
179
|
+
|
|
180
|
+
async wait(key: string | number = defaultKey) {
|
|
181
|
+
if (this.hasTasks(key)) {
|
|
182
|
+
return this.getSemaphoreInstance(key).wait();
|
|
183
|
+
} else {
|
|
184
|
+
return Promise.resolve();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
166
187
|
}
|
|
167
188
|
|
|
168
189
|
export default Semaphore;
|
package/tsconfig.json
ADDED
package/jest.config.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).promiseSemaphore=t()}(this,function(){function e(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,"string");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:String(t)}var t=/*#__PURE__*/function(){function t(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}var n,r,i=t.prototype;return i.acquire=function(){var e=this;return this.canAcquire?(this.count++,Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},i.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.count--},n=t,(r=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(t,n){for(var r=0;r<n.length;r++){var i=n[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,e(i.key),i)}}(n.prototype,r),Object.defineProperty(n,"prototype",{writable:!1}),t}(),n="_default";/*#__PURE__*/
|
|
2
|
-
return function(){function e(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=e.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=n),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(e){return void 0===e&&(e=n),this.hasSemaphoreInstance(e)||(this.semaphoreInstances[e]=new t(this.maxConcurrent)),this.semaphoreInstances[e]},r.tidy=function(e){void 0===e&&(e=n),this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]},r.canAcquire=function(e){return void 0===e&&(e=n),this.getSemaphoreInstance(e).canAcquire},r.acquire=function(e){return void 0===e&&(e=n),this.getSemaphoreInstance(e).acquire()},r.release=function(e){void 0===e&&(e=n),this.getSemaphoreInstance(e).release(),this.tidy(e)},r.count=function(e){return void 0===e&&(e=n),this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0},r.hasTasks=function(e){return void 0===e&&(e=n),this.count(e)>0},r.request=function(e,t){void 0===t&&(t=n);try{var r=this;return Promise.resolve(function(n,i){try{var o=Promise.resolve(r.acquire(t)).then(function(){return Promise.resolve(e())})}catch(e){return i(!0,e)}return o&&o.then?o.then(i.bind(null,!1),i.bind(null,!0)):i(!1,o)}(0,function(e,n){if(r.release(t),e)throw n;return n}))}catch(e){return Promise.reject(e)}},r.requestIfAvailable=function(e,t){void 0===t&&(t=n);try{return this.canAcquire(t)?Promise.resolve(this.request(e,t)):Promise.resolve(null)}catch(e){return Promise.reject(e)}},e}()});
|
|
3
|
-
//# sourceMappingURL=promise-semaphore.umd.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"promise-semaphore.umd.js","sources":["../src/index.ts"],"sourcesContent":["class SemaphoreItem {\n private queue: Array<Function>;\n private maxConcurrent: number;\n /**\n * The number of locks.\n */\n public count: number;\n\n constructor(maxConcurrent: number) {\n this.queue = [];\n this.maxConcurrent = maxConcurrent;\n this.count = 0;\n }\n\n get canAcquire(): boolean {\n return this.count < this.maxConcurrent;\n }\n\n acquire(): Promise<void> {\n if (this.canAcquire) {\n this.count++;\n return Promise.resolve();\n } else {\n return new Promise((resolve) => this.queue.push(resolve));\n }\n }\n\n release(): void {\n const resolveFunc = this.queue.shift();\n\n if (resolveFunc) {\n // Give the micro task queue a small break instead of calling resolveFunc() directly\n setTimeout(resolveFunc, 0);\n // resolveFunc()\n } else {\n this.count--;\n }\n }\n}\n\nconst defaultKey = \"_default\";\n\nclass Semaphore {\n private semaphoreInstances: Record<string | number, SemaphoreItem>;\n private maxConcurrent: number;\n\n /**\n *\n * @param {number} [maxConcurrent] The maximum number of concurrent locks.\n */\n constructor(maxConcurrent: number = 1) {\n this.semaphoreInstances = {};\n this.maxConcurrent = maxConcurrent;\n }\n\n private hasSemaphoreInstance(key: string | number = defaultKey) {\n return Boolean(this.semaphoreInstances[key]);\n }\n\n private getSemaphoreInstance(key: string | number = defaultKey) {\n if (!this.hasSemaphoreInstance(key)) {\n this.semaphoreInstances[key] = new SemaphoreItem(this.maxConcurrent);\n }\n return this.semaphoreInstances[key];\n }\n\n /**\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n private tidy(key: string | number = defaultKey): void {\n if (\n this.hasSemaphoreInstance(key) &&\n this.getSemaphoreInstance(key).count === 0\n ) {\n delete this.semaphoreInstances[key];\n }\n }\n\n /**\n * A synchronous function to determine whether a lock can be acquired.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} Returns true if the lock on `key` can be acquired, false\n * otherwise.\n */\n canAcquire(key: string | number = defaultKey): boolean {\n return this.getSemaphoreInstance(key).canAcquire;\n }\n\n /**\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n acquire(key: string | number = defaultKey) {\n return this.getSemaphoreInstance(key).acquire();\n }\n\n /**\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n release(key: string | number = defaultKey): void {\n this.getSemaphoreInstance(key).release();\n this.tidy(key);\n }\n\n /**\n * The number of active locks. Will always be less or equal to `max`.\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n */\n count(key: string | number = defaultKey): number {\n if (this.hasSemaphoreInstance(key)) {\n return this.getSemaphoreInstance(key).count;\n } else {\n return 0;\n }\n }\n\n /**\n *\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {boolean} True if the semaphore and key has locks, false otherwise.\n */\n hasTasks(key: string | number = defaultKey): boolean {\n return this.count(key) > 0;\n }\n\n /**\n *\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async request<T>(\n fn: Function,\n key: string | number = defaultKey\n ): Promise<T> {\n try {\n await this.acquire(key);\n return await fn();\n } finally {\n this.release(key);\n }\n }\n\n /**\n * Asynchronously executes `fn` if a lock can be immediately acquired.\n * Otherwise, returns null.\n *\n * @param {Function<T>} fn The function to execute.\n * @param {string | number} [key]- Optional, the semaphore key.\n * @returns {Promise<T>}\n */\n async requestIfAvailable<T>(\n fn: Function,\n key: string | number = defaultKey\n ): Promise<T | null> {\n if (this.canAcquire(key)) {\n return this.request(fn, key);\n } else {\n return null;\n }\n }\n}\n\nexport default Semaphore;\n"],"names":["SemaphoreItem","maxConcurrent","this","queue","count","_proto","prototype","acquire","_this","canAcquire","Promise","resolve","push","release","resolveFunc","shift","setTimeout","key","get","defaultKey","Semaphore","semaphoreInstances","_proto2","hasSemaphoreInstance","Boolean","getSemaphoreInstance","tidy","hasTasks","request","fn","_this2","then","_finallyRethrows","_wasThrown","_result","e","reject","requestIfAvailable"],"mappings":"6gBAAMA,0BAQJ,SAAAA,EAAYC,GAAqBC,KAPzBC,WACAF,EAAAA,KAAAA,mBAIDG,EAAAA,KAAAA,aAGLF,KAAKC,MAAQ,GACbD,KAAKD,cAAgBA,EACrBC,KAAKE,MAAQ,CACf,CAAC,QAAAC,EAAAL,EAAAM,UAIAN,OAJAK,EAMDE,QAAA,eAAOC,EAAAN,KACL,OAAIA,KAAKO,YACPP,KAAKE,QACEM,QAAQC,eAEJD,QAAQ,SAACC,GAAY,OAAAH,EAAKL,MAAMS,KAAKD,EAAQ,EAE5D,EAACN,EAEDQ,QAAA,WACE,IAAMC,EAAcZ,KAAKC,MAAMY,QAE3BD,EAEFE,WAAWF,EAAa,GAGxBZ,KAAKE,OAET,IAACJ,OAAAiB,IAAA,aAAAC,IAvBD,WACE,OAAOhB,KAAKE,MAAQF,KAAKD,aAC3B,gPAACD,CAAA,IAwBGmB,EAAa;OAEJ,WAQb,SAAAC,EAAYnB,QAAAA,IAAAA,IAAAA,EAAwB,GAACC,KAP7BmB,wBAAkB,EAAAnB,KAClBD,mBAON,EAAAC,KAAKmB,mBAAqB,CAAE,EAC5BnB,KAAKD,cAAgBA,CACvB,CAAC,IAAAqB,EAAAF,EAAAd,UA+GA,OA/GAgB,EAEOC,qBAAA,SAAqBN,GAC3B,gBAD2BA,IAAAA,EAAuBE,GAC3CK,QAAQtB,KAAKmB,mBAAmBJ,GACzC,EAACK,EAEOG,qBAAA,SAAqBR,GAI3B,gBAJ2BA,IAAAA,EAAuBE,GAC7CjB,KAAKqB,qBAAqBN,KAC7Bf,KAAKmB,mBAAmBJ,GAAO,IAAIjB,EAAcE,KAAKD,gBAE7CC,KAACmB,mBAAmBJ,EACjC,EAACK,EAMOI,KAAA,SAAKT,QAAAA,IAAAA,IAAAA,EAAuBE,GAEhCjB,KAAKqB,qBAAqBN,IACe,IAAzCf,KAAKuB,qBAAqBR,GAAKb,cAExBF,KAAKmB,mBAAmBJ,EAEnC,EAACK,EASDb,WAAA,SAAWQ,GACT,gBADSA,IAAAA,EAAuBE,GACzBjB,KAAKuB,qBAAqBR,GAAKR,UACxC,EAACa,EAMDf,QAAA,SAAQU,GACN,gBADMA,IAAAA,EAAuBE,GAClBjB,KAACuB,qBAAqBR,GAAKV,SACxC,EAACe,EAMDT,QAAA,SAAQI,QAAAA,IAAAA,IAAAA,EAAuBE,GAC7BjB,KAAKuB,qBAAqBR,GAAKJ,UAC/BX,KAAKwB,KAAKT,EACZ,EAACK,EAODlB,MAAA,SAAMa,GACJ,gBADIA,IAAAA,EAAuBE,GACvBjB,KAAKqB,qBAAqBN,QAChBQ,qBAAqBR,GAAKb,MAGvC,CACH,EAACkB,EAODK,SAAA,SAASV,GACP,gBADOA,IAAAA,EAAuBE,GACvBjB,KAAKE,MAAMa,GAAO,CAC3B,EAACK,EAQKM,QAAOA,SACXC,EACAZ,YAAAA,IAAAA,EAAuBE,GAAU,IAAA,IAAAW,EAGzB5B,KAAIQ,OAAAA,QAAAC,gCADRD,QAAAC,QACImB,EAAKvB,QAAQU,IAAIc,KAAA,WAAA,OAAArB,QAAAC,QACVkB,IACd,4FAFWG,CADR,EAGHC,SAAAA,EAAAC,GACmB,GAAlBJ,EAAKjB,QAAQI,GAAKgB,EAAAC,MAAAA,SAAAA,CAAA,GAEtB,CAAC,MAAAC,GAAAzB,OAAAA,QAAA0B,OAAAD,EAAAb,CAAAA,EAAAA,EAUKe,mBAAkB,SACtBR,EACAZ,QAAAA,IAAAA,IAAAA,EAAuBE,GAAU,IAEjC,OAAIjB,KAAKO,WAAWQ,GAClBP,QAAAC,QADET,KACU0B,QAAQC,EAAIZ,IAExBP,QAAAC,QAAO,KAEX,CAAC,MAAAwB,GAAAzB,OAAAA,QAAA0B,OAAAD,EAAA,CAAA,EAAAf,CAAA,CA1HY"}
|