@chriscdn/promise-semaphore 2.0.9 → 2.0.10
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 +44 -51
- package/__tests__/semaphore.test.ts +106 -102
- package/lib/promise-semaphore.cjs +1 -1
- package/lib/promise-semaphore.cjs.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/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,15 @@ 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.
|
|
57
53
|
|
|
58
|
-
### request function
|
|
54
|
+
### `request` function
|
|
59
55
|
|
|
60
56
|
```js
|
|
61
|
-
const results = await semaphore.request(fn [,key])
|
|
57
|
+
const results = await semaphore.request(fn [, key]);
|
|
62
58
|
```
|
|
63
59
|
|
|
64
|
-
This function reduces boilerplate when using `acquire` and `release`. It returns a promise
|
|
60
|
+
This function reduces boilerplate when using `acquire` and `release`. It returns a promise that resolves when `fn` completes. It is functionally equivalent to:
|
|
65
61
|
|
|
66
62
|
```js
|
|
67
63
|
try {
|
|
@@ -72,23 +68,21 @@ try {
|
|
|
72
68
|
}
|
|
73
69
|
```
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### requestIfAvailable function
|
|
71
|
+
### `requestIfAvailable` function
|
|
78
72
|
|
|
79
73
|
```js
|
|
80
|
-
const results = await semaphore.requestIfAvailable(fn [,key])
|
|
74
|
+
const results = await semaphore.requestIfAvailable(fn [, key]);
|
|
81
75
|
```
|
|
82
76
|
|
|
83
77
|
This is functionally equivalent to:
|
|
84
78
|
|
|
85
79
|
```js
|
|
86
|
-
const results = semaphore.canAcquire([key]
|
|
87
|
-
await semaphore.request(fn, [key])
|
|
88
|
-
null
|
|
80
|
+
const results = semaphore.canAcquire([key])
|
|
81
|
+
? await semaphore.request(fn, [key])
|
|
82
|
+
: null;
|
|
89
83
|
```
|
|
90
84
|
|
|
91
|
-
This is useful in
|
|
85
|
+
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
86
|
|
|
93
87
|
## Example 1
|
|
94
88
|
|
|
@@ -96,66 +90,66 @@ This is useful in situations when only one instance of a function block should r
|
|
|
96
90
|
import Semaphore from "@chriscdn/promise-semaphore";
|
|
97
91
|
const semaphore = new Semaphore();
|
|
98
92
|
|
|
99
|
-
//
|
|
93
|
+
// Using promises
|
|
100
94
|
semaphore
|
|
101
95
|
.acquire()
|
|
102
96
|
.then(() => {
|
|
103
|
-
// This block executes once a lock is acquired.
|
|
104
|
-
//
|
|
97
|
+
// This block executes once a lock is acquired.
|
|
98
|
+
// If already locked, it waits and executes after all preceding locks are released.
|
|
105
99
|
//
|
|
106
|
-
//
|
|
100
|
+
// Critical operations are performed here.
|
|
107
101
|
})
|
|
108
102
|
.finally(() => {
|
|
109
|
-
//
|
|
103
|
+
// The lock is released, allowing the next queued block to proceed.
|
|
110
104
|
semaphore.release();
|
|
111
105
|
});
|
|
112
106
|
|
|
113
|
-
//
|
|
107
|
+
// Using async/await
|
|
114
108
|
try {
|
|
115
109
|
await semaphore.acquire();
|
|
116
110
|
|
|
117
|
-
//
|
|
111
|
+
// Critical operations are performed here.
|
|
118
112
|
} finally {
|
|
119
113
|
semaphore.release();
|
|
120
114
|
}
|
|
121
115
|
|
|
122
|
-
//
|
|
123
|
-
semaphore.request(() => {
|
|
124
|
-
//
|
|
116
|
+
// Using the request function
|
|
117
|
+
await semaphore.request(() => {
|
|
118
|
+
// Critical operations are performed here.
|
|
125
119
|
});
|
|
126
120
|
```
|
|
127
121
|
|
|
128
122
|
## Example 2
|
|
129
123
|
|
|
130
|
-
|
|
124
|
+
Consider an asynchronous function that downloads a file and saves it to disk:
|
|
131
125
|
|
|
132
126
|
```js
|
|
133
|
-
async
|
|
127
|
+
const downloadAndSave = async (url) => {
|
|
134
128
|
const filePath = urlToFilePath(url);
|
|
135
129
|
|
|
136
130
|
if (await pathExists(filePath)) {
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
await downloadAndSaveToFilepath(url, filePath);
|
|
131
|
+
// The file is already on disk, so no action is required.
|
|
132
|
+
return filePath;
|
|
140
133
|
}
|
|
141
134
|
|
|
135
|
+
await downloadAndSaveToFilepath(url, filePath);
|
|
142
136
|
return filePath;
|
|
143
|
-
}
|
|
137
|
+
};
|
|
144
138
|
```
|
|
145
139
|
|
|
146
|
-
This works
|
|
140
|
+
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
141
|
|
|
148
|
-
This can be resolved
|
|
142
|
+
This issue can be resolved by using a `Semaphore` with the `key` parameter:
|
|
149
143
|
|
|
150
144
|
```js
|
|
151
145
|
import Semaphore from "@chriscdn/promise-semaphore";
|
|
152
146
|
const semaphore = new Semaphore();
|
|
153
147
|
|
|
154
|
-
async
|
|
148
|
+
const downloadAndSave = async (url) => {
|
|
155
149
|
try {
|
|
156
150
|
await semaphore.acquire(url);
|
|
157
151
|
|
|
158
|
-
// This block continues once a lock on url is acquired.
|
|
152
|
+
// This block continues once a lock on url is acquired. This
|
|
159
153
|
// permits multiple simulataneous downloads for different urls.
|
|
160
154
|
|
|
161
155
|
const filePath = urlToFilePath(url);
|
|
@@ -170,27 +164,26 @@ async function downloadAndSave(url) {
|
|
|
170
164
|
} finally {
|
|
171
165
|
semaphore.release(url);
|
|
172
166
|
}
|
|
173
|
-
}
|
|
167
|
+
};
|
|
174
168
|
```
|
|
175
169
|
|
|
176
|
-
|
|
170
|
+
The same outcome can be achieved using the `request` function:
|
|
177
171
|
|
|
178
172
|
```js
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const filePath = urlToFilePath(url)
|
|
173
|
+
const downloadAndSave = (url) => {
|
|
174
|
+
return semaphore.request(async () => {
|
|
175
|
+
const filePath = urlToFilePath(url);
|
|
183
176
|
|
|
184
177
|
if (await pathExists(filePath)) {
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
await downloadAndSaveToFilepath(url, filePath)
|
|
178
|
+
// The file is already on disk, so no action is required.
|
|
179
|
+
return filePath;
|
|
188
180
|
}
|
|
189
181
|
|
|
190
|
-
|
|
191
|
-
}, url)
|
|
182
|
+
await downloadAndSaveToFilepath(url, filePath);
|
|
192
183
|
|
|
193
|
-
|
|
184
|
+
return filePath;
|
|
185
|
+
}, url);
|
|
186
|
+
};
|
|
194
187
|
```
|
|
195
188
|
|
|
196
189
|
## License
|
|
@@ -1,143 +1,147 @@
|
|
|
1
|
+
import { describe, expect, test } 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
|
+
test("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
|
-
test("Semaphore 1", async () => {
|
|
18
|
-
|
|
20
|
+
test("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
|
-
test("Semaphore 2", async () => {
|
|
37
|
-
|
|
39
|
+
test("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
|
-
test("Semaphore 3", async () => {
|
|
55
|
-
|
|
57
|
+
test("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
|
-
test("Request 1", async () => {
|
|
86
|
-
|
|
88
|
+
test("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
|
-
test("Request 2", async () => {
|
|
106
|
-
|
|
108
|
+
test("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
|
-
|
|
118
|
+
semaphore.request(async () => {
|
|
119
|
+
await pause(500);
|
|
120
|
+
tester = 10;
|
|
121
|
+
});
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
await semaphore.request(async () => {
|
|
124
|
+
expect(tester).toBe(10);
|
|
125
|
+
});
|
|
122
126
|
});
|
|
123
|
-
});
|
|
124
127
|
|
|
125
|
-
test("Request 3", async () => {
|
|
126
|
-
|
|
128
|
+
test("Request 3", async () => {
|
|
129
|
+
const semaphore = new Semaphore(3);
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
let tester = 0;
|
|
129
132
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
semaphore.request(async () => {
|
|
134
|
+
await pause(1000);
|
|
135
|
+
tester = 20;
|
|
136
|
+
});
|
|
134
137
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
semaphore.request(async () => {
|
|
139
|
+
await pause(500);
|
|
140
|
+
tester = 10;
|
|
141
|
+
});
|
|
139
142
|
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
await semaphore.request(async () => {
|
|
144
|
+
expect(tester).toBe(0);
|
|
145
|
+
});
|
|
142
146
|
});
|
|
143
147
|
});
|
|
@@ -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.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}),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)}},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.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","
|
|
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.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","queue","this","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":"mSAAMA,eAQJ,WAAA,SAAAA,EAAYC,QAPJC,WAAK,EAAAC,KACLF,mBAIDG,EAAAA,KAAAA,WAGL,EAAAD,KAAKD,MAAQ,GACbC,KAAKF,cAAgBA,EACrBE,KAAKC,MAAQ,CACf,CAAC,QAAAC,EAAAL,EAAAM,iBAAAD,EAMDE,QAAA,WAAO,IAAAC,EACLL,KAAA,OAAIA,KAAKM,YACPN,KAAKC,QACEM,QAAQC,WAER,IAAID,QAAQ,SAACC,GAAO,OAAKH,EAAKN,MAAMU,KAAKD,EAAQ,EAE5D,EAACN,EAEDQ,QAAA,WACE,IAAMC,EAAcX,KAAKD,MAAMa,QAE3BD,EAEFE,WAAWF,EAAa,GAGxBX,KAAKC,OAET,IAACJ,KAAA,CAAA,CAAAiB,IAAAC,aAAAA,IAvBD,WACE,OAAWf,KAACC,MAAQD,KAAKF,aAC3B,kPARA,GAgCIkB,EAAa,uCAEJ,WAQb,SAAAC,EAAYnB,QAAAA,IAAAA,IAAAA,EAAwB,GAACE,KAP7BkB,wBACApB,EAAAA,KAAAA,qBAONE,KAAKkB,mBAAqB,GAC1BlB,KAAKF,cAAgBA,CACvB,CAAC,IAAAqB,EAAAF,EAAAd,UA+GA,OA/GAgB,EAEOC,qBAAA,SAAqBN,GAC3B,YAD2BA,IAAAA,IAAAA,EAAuBE,GAC3CK,QAAQrB,KAAKkB,mBAAmBJ,GACzC,EAACK,EAEOG,qBAAA,SAAqBR,GAI3B,gBAJ2BA,IAAAA,EAAuBE,GAC7ChB,KAAKoB,qBAAqBN,KAC7Bd,KAAKkB,mBAAmBJ,GAAO,IAAIjB,EAAcG,KAAKF,qBAE5CoB,mBAAmBJ,EACjC,EAACK,EAMOI,KAAA,SAAKT,YAAAA,IAAAA,EAAuBE,GAEhChB,KAAKoB,qBAAqBN,IACe,IAAzCd,KAAKsB,qBAAqBR,GAAKb,cAEpBD,KAACkB,mBAAmBJ,EAEnC,EAACK,EASDb,WAAA,SAAWQ,GACT,gBADSA,IAAAA,EAAuBE,GACzBhB,KAAKsB,qBAAqBR,GAAKR,UACxC,EAACa,EAMDf,QAAA,SAAQU,GACN,gBADMA,IAAAA,EAAuBE,GAClBhB,KAACsB,qBAAqBR,GAAKV,SACxC,EAACe,EAMDT,QAAA,SAAQI,YAAAA,IAAAA,EAAuBE,GAC7BhB,KAAKsB,qBAAqBR,GAAKJ,UAC/BV,KAAKuB,KAAKT,EACZ,EAACK,EAODlB,MAAA,SAAMa,GACJ,YADIA,IAAAA,IAAAA,EAAuBE,GACvBhB,KAAKoB,qBAAqBN,GACrBd,KAAKsB,qBAAqBR,GAAKb,MAE/B,CAEX,EAACkB,EAODK,SAAA,SAASV,GACP,YADOA,IAAAA,IAAAA,EAAuBE,GACnBhB,KAACC,MAAMa,GAAO,CAC3B,EAACK,EAQKM,iBACJC,EACAZ,QAAAA,IAAAA,IAAAA,EAAuBE,GAAU,IAAAW,IAAAA,EAGzB3B,KAAI,OAAAO,QAAAC,gCADRD,QAAAC,QACImB,EAAKvB,QAAQU,IAAIc,uBAAArB,QAAAC,QACVkB,IAAI,4FADPG,CAAA,EAEXC,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,YAAAA,IAAAA,EAAuBE,GAAU,IAEjC,OAAIhB,KAAKM,WAAWQ,GAClBP,QAAAC,QADER,KACUyB,QAAQC,EAAIZ,IAExBP,QAAAC,QAAO,KAEX,CAAC,MAAAwB,GAAA,OAAAzB,QAAA0B,OAAAD,EAAA,CAAA,EAAAf,CAAA,CA1HY"}
|
|
@@ -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.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}),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)}},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.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","
|
|
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.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","queue","this","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":"mSAAMA,eAQJ,WAAA,SAAAA,EAAYC,QAPJC,WAAK,EAAAC,KACLF,mBAIDG,EAAAA,KAAAA,WAGL,EAAAD,KAAKD,MAAQ,GACbC,KAAKF,cAAgBA,EACrBE,KAAKC,MAAQ,CACf,CAAC,QAAAC,EAAAL,EAAAM,iBAAAD,EAMDE,QAAA,WAAO,IAAAC,EACLL,KAAA,OAAIA,KAAKM,YACPN,KAAKC,QACEM,QAAQC,WAER,IAAID,QAAQ,SAACC,GAAO,OAAKH,EAAKN,MAAMU,KAAKD,EAAQ,EAE5D,EAACN,EAEDQ,QAAA,WACE,IAAMC,EAAcX,KAAKD,MAAMa,QAE3BD,EAEFE,WAAWF,EAAa,GAGxBX,KAAKC,OAET,IAACJ,KAAA,CAAA,CAAAiB,IAAAC,aAAAA,IAvBD,WACE,OAAWf,KAACC,MAAQD,KAAKF,aAC3B,kPARA,GAgCIkB,EAAa,WAEbC,eAAS,WAQb,SAAAA,EAAYnB,QAAAA,IAAAA,IAAAA,EAAwB,GAACE,KAP7BkB,wBACApB,EAAAA,KAAAA,qBAONE,KAAKkB,mBAAqB,GAC1BlB,KAAKF,cAAgBA,CACvB,CAAC,IAAAqB,EAAAF,EAAAd,UA+GA,OA/GAgB,EAEOC,qBAAA,SAAqBN,GAC3B,YAD2BA,IAAAA,IAAAA,EAAuBE,GAC3CK,QAAQrB,KAAKkB,mBAAmBJ,GACzC,EAACK,EAEOG,qBAAA,SAAqBR,GAI3B,gBAJ2BA,IAAAA,EAAuBE,GAC7ChB,KAAKoB,qBAAqBN,KAC7Bd,KAAKkB,mBAAmBJ,GAAO,IAAIjB,EAAcG,KAAKF,qBAE5CoB,mBAAmBJ,EACjC,EAACK,EAMOI,KAAA,SAAKT,YAAAA,IAAAA,EAAuBE,GAEhChB,KAAKoB,qBAAqBN,IACe,IAAzCd,KAAKsB,qBAAqBR,GAAKb,cAEpBD,KAACkB,mBAAmBJ,EAEnC,EAACK,EASDb,WAAA,SAAWQ,GACT,gBADSA,IAAAA,EAAuBE,GACzBhB,KAAKsB,qBAAqBR,GAAKR,UACxC,EAACa,EAMDf,QAAA,SAAQU,GACN,gBADMA,IAAAA,EAAuBE,GAClBhB,KAACsB,qBAAqBR,GAAKV,SACxC,EAACe,EAMDT,QAAA,SAAQI,YAAAA,IAAAA,EAAuBE,GAC7BhB,KAAKsB,qBAAqBR,GAAKJ,UAC/BV,KAAKuB,KAAKT,EACZ,EAACK,EAODlB,MAAA,SAAMa,GACJ,YADIA,IAAAA,IAAAA,EAAuBE,GACvBhB,KAAKoB,qBAAqBN,GACrBd,KAAKsB,qBAAqBR,GAAKb,MAE/B,CAEX,EAACkB,EAODK,SAAA,SAASV,GACP,YADOA,IAAAA,IAAAA,EAAuBE,GACnBhB,KAACC,MAAMa,GAAO,CAC3B,EAACK,EAQKM,iBACJC,EACAZ,QAAAA,IAAAA,IAAAA,EAAuBE,GAAU,IAAAW,IAAAA,EAGzB3B,KAAI,OAAAO,QAAAC,gCADRD,QAAAC,QACImB,EAAKvB,QAAQU,IAAIc,uBAAArB,QAAAC,QACVkB,IAAI,4FADPG,CAAA,EAEXC,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,YAAAA,IAAAA,EAAuBE,GAAU,IAEjC,OAAIhB,KAAKM,WAAWQ,GAClBP,QAAAC,QADER,KACUyB,QAAQC,EAAIZ,IAExBP,QAAAC,QAAO,KAEX,CAAC,MAAAwB,GAAA,OAAAzB,QAAA0B,OAAAD,EAAA,CAAA,EAAAf,CAAA,CA1HY"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chriscdn/promise-semaphore",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.10",
|
|
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.0.7"
|
|
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/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"}
|