@chriscdn/promise-semaphore 2.0.6 → 2.0.8
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 +37 -37
- package/jest.config.js +4 -4
- 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/lib/promise-semaphore.umd.js +1 -1
- package/lib/promise-semaphore.umd.js.map +1 -1
- package/package.json +7 -3
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -25,8 +25,8 @@ Version 2 adds TypeScript and better inline documentation. The API remains the s
|
|
|
25
25
|
### Create an instance
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
|
-
import Semaphore from
|
|
29
|
-
const semaphore = new Semaphore([maxConcurrent])
|
|
28
|
+
import Semaphore from "@chriscdn/promise-semaphore";
|
|
29
|
+
const semaphore = new Semaphore([maxConcurrent]);
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
The `maxConcurrent` parameter is optional, and defaults to `1` (making it an exclusive lock or _binary semaphore_). Use an integer value greater than one to limit how many times the code block can be simultaneously executing from separate iterations of the event loop.
|
|
@@ -34,7 +34,7 @@ The `maxConcurrent` parameter is optional, and defaults to `1` (making it an exc
|
|
|
34
34
|
### Acquire a lock
|
|
35
35
|
|
|
36
36
|
```js
|
|
37
|
-
semaphore.acquire([key])
|
|
37
|
+
semaphore.acquire([key]);
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
This returns a `Promise`, which resolves once a lock has been acquired. The `key` parameter is optional and permits the same `Semaphore` instance to be used in different contexts. See the second example.
|
|
@@ -42,7 +42,7 @@ This returns a `Promise`, which resolves once a lock has been acquired. The `key
|
|
|
42
42
|
### Release a lock
|
|
43
43
|
|
|
44
44
|
```js
|
|
45
|
-
semaphore.release([key])
|
|
45
|
+
semaphore.release([key]);
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The `release` call should be executed from a `finally` block (whether using promises or a try/catch block) to guarantee it gets called.
|
|
@@ -50,7 +50,7 @@ The `release` call should be executed from a `finally` block (whether using prom
|
|
|
50
50
|
### Check if a lock can be acquired
|
|
51
51
|
|
|
52
52
|
```js
|
|
53
|
-
semaphore.canAcquire([key])
|
|
53
|
+
semaphore.canAcquire([key]);
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
This method is synchronous, and returns `true` if a lock can be immediately acquired, `false` otherwise.
|
|
@@ -65,10 +65,10 @@ This function reduces boilerplate when using `acquire` and `release`. It returns
|
|
|
65
65
|
|
|
66
66
|
```js
|
|
67
67
|
try {
|
|
68
|
-
await semaphore.acquire([key])
|
|
69
|
-
const results = await fn()
|
|
68
|
+
await semaphore.acquire([key]);
|
|
69
|
+
const results = await fn();
|
|
70
70
|
} finally {
|
|
71
|
-
semaphore.release([key])
|
|
71
|
+
semaphore.release([key]);
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
@@ -84,17 +84,17 @@ This is functionally equivalent to:
|
|
|
84
84
|
|
|
85
85
|
```js
|
|
86
86
|
const results = semaphore.canAcquire([key] ?
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
await semaphore.request(fn, [key]) :
|
|
88
|
+
null
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
This is useful in situations
|
|
91
|
+
This is useful in situations when only one instance of a function block should run at a time, while discarding other attempts to execute the block. E.g., a button is repeatedly clicked.
|
|
92
92
|
|
|
93
93
|
## Example 1
|
|
94
94
|
|
|
95
95
|
```js
|
|
96
|
-
import Semaphore from
|
|
97
|
-
const semaphore = new Semaphore()
|
|
96
|
+
import Semaphore from "@chriscdn/promise-semaphore";
|
|
97
|
+
const semaphore = new Semaphore();
|
|
98
98
|
|
|
99
99
|
// using promises
|
|
100
100
|
semaphore
|
|
@@ -107,22 +107,22 @@ semaphore
|
|
|
107
107
|
})
|
|
108
108
|
.finally(() => {
|
|
109
109
|
// release the lock permitting the next queued block to continue
|
|
110
|
-
semaphore.release()
|
|
111
|
-
})
|
|
110
|
+
semaphore.release();
|
|
111
|
+
});
|
|
112
112
|
|
|
113
113
|
// or, using async/await
|
|
114
114
|
try {
|
|
115
|
-
await semaphore.acquire()
|
|
115
|
+
await semaphore.acquire();
|
|
116
116
|
|
|
117
117
|
// do your critical stuff here
|
|
118
118
|
} finally {
|
|
119
|
-
semaphore.release()
|
|
119
|
+
semaphore.release();
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// or, using the request function
|
|
123
123
|
semaphore.request(() => {
|
|
124
124
|
// do your critical stuff here
|
|
125
|
-
})
|
|
125
|
+
});
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
## Example 2
|
|
@@ -131,15 +131,15 @@ Say you have an asynchronous function to download a file and save it to disk:
|
|
|
131
131
|
|
|
132
132
|
```js
|
|
133
133
|
async function downloadAndSave(url) {
|
|
134
|
-
const filePath = urlToFilePath(url)
|
|
134
|
+
const filePath = urlToFilePath(url);
|
|
135
135
|
|
|
136
136
|
if (await pathExists(filePath)) {
|
|
137
137
|
// the file is on disk, so no action is required
|
|
138
138
|
} else {
|
|
139
|
-
await downloadAndSaveToFilepath(url, filePath)
|
|
139
|
+
await downloadAndSaveToFilepath(url, filePath);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
return filePath
|
|
142
|
+
return filePath;
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
@@ -148,27 +148,27 @@ This works until a process calls `downloadAndSave()` multiple times in short suc
|
|
|
148
148
|
This can be resolved with a `Semaphore` instance using the `key` parameter:
|
|
149
149
|
|
|
150
150
|
```js
|
|
151
|
-
import Semaphore from
|
|
152
|
-
const semaphore = new Semaphore()
|
|
151
|
+
import Semaphore from "@chriscdn/promise-semaphore";
|
|
152
|
+
const semaphore = new Semaphore();
|
|
153
153
|
|
|
154
154
|
async function downloadAndSave(url) {
|
|
155
155
|
try {
|
|
156
|
-
await semaphore.acquire(url)
|
|
156
|
+
await semaphore.acquire(url);
|
|
157
157
|
|
|
158
158
|
// This block continues once a lock on url is acquired. This
|
|
159
159
|
// permits multiple simulataneous downloads for different urls.
|
|
160
160
|
|
|
161
|
-
const filePath = urlToFilePath(url)
|
|
161
|
+
const filePath = urlToFilePath(url);
|
|
162
162
|
|
|
163
163
|
if (await pathExists(filePath)) {
|
|
164
164
|
// the file is on disk, so no action is required
|
|
165
165
|
} else {
|
|
166
|
-
await downloadAndSaveToFilepath(url, filePath)
|
|
166
|
+
await downloadAndSaveToFilepath(url, filePath);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
return filePath
|
|
169
|
+
return filePath;
|
|
170
170
|
} finally {
|
|
171
|
-
semaphore.release(url)
|
|
171
|
+
semaphore.release(url);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
```
|
|
@@ -178,17 +178,17 @@ Alternatively, this can be accomplished with the `request` function:
|
|
|
178
178
|
```js
|
|
179
179
|
async function downloadAndSave(url) {
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
return semaphore.request(() => {
|
|
182
|
+
const filePath = urlToFilePath(url)
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
if (await pathExists(filePath)) {
|
|
185
|
+
// the file is on disk, so no action is required
|
|
186
|
+
} else {
|
|
187
|
+
await downloadAndSaveToFilepath(url, filePath)
|
|
188
|
+
}
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
return filePath
|
|
191
|
+
}, url)
|
|
192
192
|
|
|
193
193
|
}
|
|
194
194
|
```
|
package/jest.config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
-
|
|
3
|
-
preset:
|
|
4
|
-
testEnvironment:
|
|
5
|
-
};
|
|
2
|
+
export default {
|
|
3
|
+
preset: "ts-jest",
|
|
4
|
+
testEnvironment: "node",
|
|
5
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=/*#__PURE__*/function(){function e(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}var t,n,r=e.prototype;return r.acquire=function(){var e=this;return this.canAcquire?(this.count++,Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},r.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.count--},t=e,(n=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,"symbol"==typeof(i=function(e,t){if("object"!=typeof e||null===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)}(r.key))?i:String(i),r)}var i}(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t="_default";module.exports=/*#__PURE__*/function(){function n(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=n.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=t),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(n){return void 0===n&&(n=t),this.hasSemaphoreInstance(n)||(this.semaphoreInstances[n]=new e(this.maxConcurrent)),this.semaphoreInstances[n]},r.tidy=function(e){void 0===e&&(e=t),this.hasSemaphoreInstance(e)&&0
|
|
1
|
+
var e=/*#__PURE__*/function(){function e(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}var t,n,r=e.prototype;return r.acquire=function(){var e=this;return this.canAcquire?(this.count++,Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},r.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.count--},t=e,(n=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,"symbol"==typeof(i=function(e,t){if("object"!=typeof e||null===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)}(r.key))?i:String(i),r)}var i}(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t="_default";module.exports=/*#__PURE__*/function(){function n(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=n.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=t),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(n){return void 0===n&&(n=t),this.hasSemaphoreInstance(n)||(this.semaphoreInstances[n]=new e(this.maxConcurrent)),this.semaphoreInstances[n]},r.tidy=function(e){void 0===e&&(e=t),this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]},r.canAcquire=function(e){return void 0===e&&(e=t),this.getSemaphoreInstance(e).canAcquire},r.acquire=function(e){return void 0===e&&(e=t),this.getSemaphoreInstance(e).acquire()},r.release=function(e){void 0===e&&(e=t),this.getSemaphoreInstance(e).release(),this.tidy(e)},r.count=function(e){return void 0===e&&(e=t),this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0},r.hasTasks=function(e){return void 0===e&&(e=t),this.count(e)>0},r.request=function(e,n){void 0===n&&(n=t);try{var r=this;return Promise.resolve(function(t,i){try{var o=Promise.resolve(r.acquire(n)).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,t){if(r.release(n),e)throw t;return t}))}catch(e){return Promise.reject(e)}},r.requestIfAvailable=function(e,n){void 0===n&&(n=t);try{return this.canAcquire(n)?Promise.resolve(this.request(e,n)):Promise.resolve(null)}catch(e){return Promise.reject(e)}},n}();
|
|
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
|
|
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","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":"IAAMA,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,mgBAACD,CAAA,IAwBGmB,EAAa,uCAEJ,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"}
|
|
@@ -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.count++,Promise.resolve()):new Promise(e=>this.queue.push(e))}release(){const e=this.queue.shift();e?setTimeout(e,0):this.count--}}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
|
|
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.count++,Promise.resolve()):new Promise(e=>this.queue.push(e))}release(){const e=this.queue.shift();e?setTimeout(e,0):this.count--}}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}}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.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
|
|
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.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","constructor","maxConcurrent","queue","count","this","canAcquire","acquire","Promise","resolve","push","release","resolveFunc","shift","setTimeout","defaultKey","Semaphore","semaphoreInstances","hasSemaphoreInstance","key","Boolean","getSemaphoreInstance","tidy","hasTasks","request","fn","requestIfAvailable"],"mappings":"AAAA,MAAMA,EAQJC,WAAAA,CAAYC,GAPJC,KAAAA,WACAD,EAAAA,KAAAA,0BAIDE,WAAK,EAGVC,KAAKF,MAAQ,GACbE,KAAKH,cAAgBA,EACrBG,KAAKD,MAAQ,CACf,CAEA,cAAIE,GACF,YAAYF,MAAQC,KAAKH,aAC3B,CAEAK,OAAAA,GACE,OAAIF,KAAKC,YACPD,KAAKD,QACEI,QAAQC,WAER,IAAID,QAASC,GAAYJ,KAAKF,MAAMO,KAAKD,GAEpD,CAEAE,OAAAA,GACE,MAAMC,EAAcP,KAAKF,MAAMU,QAE3BD,EAEFE,WAAWF,EAAa,GAGxBP,KAAKD,OAET,EAGF,MAAMW,EAAa,WAEnB,MAAMC,EAQJf,WAAAA,CAAYC,EAAwB,GAP5Be,KAAAA,+BACAf,mBAAa,EAOnBG,KAAKY,mBAAqB,CAAA,EAC1BZ,KAAKH,cAAgBA,CACvB,CAEQgB,oBAAAA,CAAqBC,EAAuBJ,GAClD,OAAOK,QAAQf,KAAKY,mBAAmBE,GACzC,CAEQE,oBAAAA,CAAqBF,EAAuBJ,GAIlD,OAHKV,KAAKa,qBAAqBC,KAC7Bd,KAAKY,mBAAmBE,GAAO,IAAInB,EAAcK,KAAKH,gBAEjDG,KAAKY,mBAAmBE,EACjC,CAMQG,IAAAA,CAAKH,EAAuBJ,GAEhCV,KAAKa,qBAAqBC,IACe,IAAzCd,KAAKgB,qBAAqBF,GAAKf,cAExBC,KAAKY,mBAAmBE,EAEnC,CASAb,UAAAA,CAAWa,EAAuBJ,GAChC,OAAWV,KAACgB,qBAAqBF,GAAKb,UACxC,CAMAC,OAAAA,CAAQY,EAAuBJ,GAC7B,OAAOV,KAAKgB,qBAAqBF,GAAKZ,SACxC,CAMAI,OAAAA,CAAQQ,EAAuBJ,GAC7BV,KAAKgB,qBAAqBF,GAAKR,UAC/BN,KAAKiB,KAAKH,EACZ,CAOAf,KAAAA,CAAMe,EAAuBJ,GAC3B,OAAIV,KAAKa,qBAAqBC,GACjBd,KAACgB,qBAAqBF,GAAKf,MAE/B,CAEX,CAOAmB,QAAAA,CAASJ,EAAuBJ,GAC9B,OAAWV,KAACD,MAAMe,GAAO,CAC3B,CAQA,aAAMK,CACJC,EACAN,EAAuBJ,GAEvB,IAEE,aADUV,KAACE,QAAQY,SACNM,GACd,CAAA,QACCpB,KAAKM,QAAQQ,EACd,CACH,CAUA,wBAAMO,CACJD,EACAN,EAAuBJ,GAEvB,OAAIV,KAAKC,WAAWa,GACPd,KAACmB,QAAQC,EAAIN,GAEjB,IAEX"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=/*#__PURE__*/function(){function e(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}var t,n,r=e.prototype;return r.acquire=function(){var e=this;return this.canAcquire?(this.count++,Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},r.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.count--},t=e,(n=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,"symbol"==typeof(i=function(e,t){if("object"!=typeof e||null===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)}(r.key))?i:String(i),r)}var i}(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t="_default",n=/*#__PURE__*/function(){function n(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=n.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=t),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(n){return void 0===n&&(n=t),this.hasSemaphoreInstance(n)||(this.semaphoreInstances[n]=new e(this.maxConcurrent)),this.semaphoreInstances[n]},r.tidy=function(e){void 0===e&&(e=t),this.hasSemaphoreInstance(e)&&0
|
|
1
|
+
var e=/*#__PURE__*/function(){function e(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}var t,n,r=e.prototype;return r.acquire=function(){var e=this;return this.canAcquire?(this.count++,Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},r.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.count--},t=e,(n=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,"symbol"==typeof(i=function(e,t){if("object"!=typeof e||null===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)}(r.key))?i:String(i),r)}var i}(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t="_default",n=/*#__PURE__*/function(){function n(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=n.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=t),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(n){return void 0===n&&(n=t),this.hasSemaphoreInstance(n)||(this.semaphoreInstances[n]=new e(this.maxConcurrent)),this.semaphoreInstances[n]},r.tidy=function(e){void 0===e&&(e=t),this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]},r.canAcquire=function(e){return void 0===e&&(e=t),this.getSemaphoreInstance(e).canAcquire},r.acquire=function(e){return void 0===e&&(e=t),this.getSemaphoreInstance(e).acquire()},r.release=function(e){void 0===e&&(e=t),this.getSemaphoreInstance(e).release(),this.tidy(e)},r.count=function(e){return void 0===e&&(e=t),this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0},r.hasTasks=function(e){return void 0===e&&(e=t),this.count(e)>0},r.request=function(e,n){void 0===n&&(n=t);try{var r=this;return Promise.resolve(function(t,i){try{var o=Promise.resolve(r.acquire(n)).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,t){if(r.release(n),e)throw t;return t}))}catch(e){return Promise.reject(e)}},r.requestIfAvailable=function(e,n){void 0===n&&(n=t);try{return this.canAcquire(n)?Promise.resolve(this.request(e,n)):Promise.resolve(null)}catch(e){return Promise.reject(e)}},n}();export{n 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
|
|
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","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":"IAAMA,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,mgBAACD,CAAA,IAwBGmB,EAAa,WAEbC,eAAS,WAQb,SAAAA,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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
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(){var e=/*#__PURE__*/function(){function e(e){this.queue=void 0,this.maxConcurrent=void 0,this.count=void 0,this.queue=[],this.maxConcurrent=e,this.count=0}var t,n,r=e.prototype;return r.acquire=function(){var e=this;return this.canAcquire?(this.count++,Promise.resolve()):new Promise(function(t){return e.queue.push(t)})},r.release=function(){var e=this.queue.shift();e?setTimeout(e,0):this.count--},t=e,(n=[{key:"canAcquire",get:function(){return this.count<this.maxConcurrent}}])&&function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,"symbol"==typeof(i=function(e,t){if("object"!=typeof e||null===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)}(r.key))?i:String(i),r)}var i}(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t="_default";/*#__PURE__*/
|
|
2
|
-
return function(){function n(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=n.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=t),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(n){return void 0===n&&(n=t),this.hasSemaphoreInstance(n)||(this.semaphoreInstances[n]=new e(this.maxConcurrent)),this.semaphoreInstances[n]},r.tidy=function(e){void 0===e&&(e=t),this.hasSemaphoreInstance(e)&&0
|
|
2
|
+
return function(){function n(e){void 0===e&&(e=1),this.semaphoreInstances=void 0,this.maxConcurrent=void 0,this.semaphoreInstances={},this.maxConcurrent=e}var r=n.prototype;return r.hasSemaphoreInstance=function(e){return void 0===e&&(e=t),Boolean(this.semaphoreInstances[e])},r.getSemaphoreInstance=function(n){return void 0===n&&(n=t),this.hasSemaphoreInstance(n)||(this.semaphoreInstances[n]=new e(this.maxConcurrent)),this.semaphoreInstances[n]},r.tidy=function(e){void 0===e&&(e=t),this.hasSemaphoreInstance(e)&&0===this.getSemaphoreInstance(e).count&&delete this.semaphoreInstances[e]},r.canAcquire=function(e){return void 0===e&&(e=t),this.getSemaphoreInstance(e).canAcquire},r.acquire=function(e){return void 0===e&&(e=t),this.getSemaphoreInstance(e).acquire()},r.release=function(e){void 0===e&&(e=t),this.getSemaphoreInstance(e).release(),this.tidy(e)},r.count=function(e){return void 0===e&&(e=t),this.hasSemaphoreInstance(e)?this.getSemaphoreInstance(e).count:0},r.hasTasks=function(e){return void 0===e&&(e=t),this.count(e)>0},r.request=function(e,n){void 0===n&&(n=t);try{var r=this;return Promise.resolve(function(t,i){try{var o=Promise.resolve(r.acquire(n)).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,t){if(r.release(n),e)throw t;return t}))}catch(e){return Promise.reject(e)}},r.requestIfAvailable=function(e,n){void 0===n&&(n=t);try{return this.canAcquire(n)?Promise.resolve(this.request(e,n)):Promise.resolve(null)}catch(e){return Promise.reject(e)}},n}()});
|
|
3
3
|
//# sourceMappingURL=promise-semaphore.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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":"uOAAMA,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,mgBAACD,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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chriscdn/promise-semaphore",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
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>",
|
|
@@ -17,10 +17,14 @@
|
|
|
17
17
|
"types": "./lib/index.d.ts",
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "rm -rf ./lib/ && microbundle",
|
|
20
|
-
"dev": "microbundle watch"
|
|
20
|
+
"dev": "microbundle watch",
|
|
21
|
+
"test": "jest"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"
|
|
24
|
+
"@types/jest": "^29.5.7",
|
|
25
|
+
"jest": "^29.7.0",
|
|
26
|
+
"microbundle": "^0.15.1",
|
|
27
|
+
"ts-jest": "^29.1.1"
|
|
24
28
|
},
|
|
25
29
|
"keywords": [
|
|
26
30
|
"promise",
|
package/src/index.ts
CHANGED
|
@@ -71,7 +71,7 @@ class Semaphore {
|
|
|
71
71
|
private tidy(key: string | number = defaultKey): void {
|
|
72
72
|
if (
|
|
73
73
|
this.hasSemaphoreInstance(key) &&
|
|
74
|
-
this.getSemaphoreInstance(key).count
|
|
74
|
+
this.getSemaphoreInstance(key).count === 0
|
|
75
75
|
) {
|
|
76
76
|
delete this.semaphoreInstances[key];
|
|
77
77
|
}
|