Skip to content

Commit 6868ff7

Browse files
committed
Bump version to v3.1.2: update dev dependencies, add Prettier for formatting, improve README
1 parent c444bc6 commit 6868ff7

11 files changed

+916
-892
lines changed

.prettierrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"endOfLine": "lf",
3+
"printWidth": 100,
4+
"proseWrap": "never",
5+
"singleQuote": true,
6+
"useTabs": false,
7+
"tabWidth": 2
8+
}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ If your use case requires a concurrency of 1, consider using the lock variant of
3434

3535
## Key Features :sparkles:<a id="key-features"></a>
3636

37-
- __Backpressure Control__: Ideal for job workers and background services. Concurrency control alone isn't sufficient to ensure stability and performance if backpressure control is overlooked. Without backpressure control, the heap can become overloaded, resulting in space complexity of O(*semaphore-slots* + *pending-jobs*) instead of O(*semaphore-slots*).
38-
- __Graceful Termination__: Await the completion of all currently executing jobs via the `waitForAllExecutingJobsToComplete` method.
37+
- __Backpressure Control :vertical_traffic_light:__: Ideal for job workers and background services. Concurrency control alone isn't sufficient to ensure stability and performance if backpressure control is overlooked. Without backpressure control, the heap can become overloaded, resulting in space complexity of O(*semaphore-slots* + *pending-jobs*) instead of O(*semaphore-slots*).
38+
- __Graceful & Deterministic Teardown :hourglass_flowing_sand:__: Await the completion of all currently executing jobs via the `waitForAllExecutingJobsToComplete` method. This guarantees **smooth resource cleanup**, making it well-suited for production environments (e.g., `onModuleDestroy` in NestJS) and maintaining a clean state between unit tests.
3939
- __High Efficiency :gear:__: All state-altering operations have a constant time complexity, O(1).
4040
- __Comprehensive documentation :books:__: The class is thoroughly documented, enabling IDEs to provide helpful tooltips that enhance the coding experience.
4141
- __Robust Error Handling__: Uncaught errors from background jobs triggered by `startExecution` are captured and can be accessed using the `extractUncaughtErrors` method.

dist/zero-backpressure-semaphore.d.ts

Lines changed: 36 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
export type SemaphoreJob<T> = () => Promise<T>;
22
/**
3-
* ZeroBackpressureSemaphore
4-
*
53
* The `ZeroBackpressureSemaphore` class implements a semaphore for Node.js projects, allowing users
64
* to limit the number of concurrently executing jobs. This implementation does not queue pending
75
* jobs, thereby eliminating backpressure. As a result, users have better control over memory
@@ -24,7 +22,7 @@ export type SemaphoreJob<T> = () => Promise<T>;
2422
* methods, reminiscent of the RAII idiom in C++.
2523
* Method names are chosen to clearly convey their functionality.
2624
*
27-
* ### Graceful Termination
25+
* ### Graceful Teardown
2826
* All the job execution promises are tracked by the semaphore instance, ensuring no dangling promises.
2927
* This enables graceful termination via the `waitForAllExecutingJobsToComplete` method, in scenarios
3028
* where it is essential to ensure that all the currently executing or pending jobs are fully processed
@@ -49,8 +47,6 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
4947
private _notifyAvailableSlotExists?;
5048
private _uncaughtErrors;
5149
/**
52-
* Constructor.
53-
*
5450
* Initializes the semaphore with the specified maximum number of concurrently
5551
* executing jobs. This sets up the internal structures to enforce the concurrency
5652
* limit for job execution.
@@ -60,26 +56,18 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
6056
*/
6157
constructor(maxConcurrentJobs: number);
6258
/**
63-
* maxConcurrentJobs
64-
*
6559
* @returns The maximum number of concurrent jobs as specified in the constructor.
6660
*/
6761
get maxConcurrentJobs(): number;
6862
/**
69-
* isAvailable
70-
*
7163
* @returns True if there is an available job slot, otherwise false.
7264
*/
7365
get isAvailable(): boolean;
7466
/**
75-
* amountOfCurrentlyExecutingJobs
76-
*
7767
* @returns The number of jobs currently being executed by the semaphore.
7868
*/
7969
get amountOfCurrentlyExecutingJobs(): number;
8070
/**
81-
* amountOfUncaughtErrors
82-
*
8371
* Indicates the number of uncaught errors from background jobs triggered by `startExecution`,
8472
* that are currently stored by the instance.
8573
* These errors have not yet been extracted using `extractUncaughtErrors`.
@@ -91,10 +79,8 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
9179
*/
9280
get amountOfUncaughtErrors(): number;
9381
/**
94-
* startExecution
95-
*
96-
* This method resolves once the given job has *started* its execution, indicating that the
97-
* semaphore has become available (i.e., allotted a slot for the job).
82+
* Resolves once the given job has *started* its execution, indicating that the semaphore has
83+
* become available (i.e., allotted a slot for the job).
9884
* Users can leverage this to prevent backpressure of pending jobs:
9985
* If the semaphore is too busy to start a given job `X`, there is no reason to create another
10086
* job `Y` until `X` has started.
@@ -103,7 +89,7 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
10389
* value is expected. It promotes a just-in-time approach, on which each job is pending execution
10490
* only when no other job is, thereby eliminating backpressure and reducing memory footprint.
10591
*
106-
* ### Graceful Termination
92+
* ### Graceful Teardown
10793
* Method `waitForAllExecutingJobsToComplete` complements the typical use-cases of `startExecution`.
10894
* It can be used to perform post-processing, after all the currently-executing jobs have completed.
10995
*
@@ -112,34 +98,30 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
11298
* `extractUncaughtError` method. Users are encouraged to specify a custom `UncaughtErrorType`
11399
* generic parameter to the class if jobs may throw errors.
114100
*
115-
* @param backgroundJob - The job to be executed once the semaphore is available.
101+
* @param backgroundJob The job to be executed once the semaphore is available.
116102
* @returns A promise that resolves when the job starts execution.
117103
*/
118104
startExecution(backgroundJob: SemaphoreJob<T>): Promise<void>;
119105
/**
120-
* waitForCompletion
121-
*
122-
* This method executes the given job in a controlled manner, once the semaphore is available.
123-
* It resolves or rejects when the job finishes execution, returning the job's value or propagating
124-
* any error it may throw.
106+
* Executes the given job in a controlled manner, once the semaphore is available.
107+
* It resolves or rejects when the job finishes execution, returning the job's value or
108+
* propagating any error it may throw.
125109
*
126-
* This method is useful when the flow depends on a job's execution to proceed, such as needing
127-
* its return value or handling any errors it may throw.
110+
* This method is useful when the flow depends on a job's execution to proceed, such as
111+
* needing its return value or handling any errors it may throw.
128112
*
129113
* ### Example Use Case
130114
* Suppose you have a route handler that needs to perform a specific code block with limited
131115
* concurrency (e.g., database access) due to external constraints, such as throttling limits.
132116
* This method allows you to execute the job with controlled concurrency. Once the job resolves
133117
* or rejects, you can continue the route handler's flow based on the result.
134118
*
135-
* @param job - The job to be executed once the semaphore is available.
136-
* @throws - Error thrown by the job itself.
119+
* @param job The job to be executed once the semaphore is available.
120+
* @throws Error thrown by the job itself.
137121
* @returns A promise that resolves with the job's return value or rejects with its error.
138122
*/
139123
waitForCompletion(job: SemaphoreJob<T>): Promise<T>;
140124
/**
141-
* waitForAllExecutingJobsToComplete
142-
*
143125
* Waits for all **currently executing jobs** to finish, ensuring that all active promises
144126
* have either resolved or rejected before proceeding. This enables graceful termination in
145127
* scenarios such as:
@@ -150,27 +132,25 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
150132
* By default, this method only waits for jobs that are already **executing** at the time of
151133
* invocation. In other words, the default behavior does **not** consider potential jobs that
152134
* are still queued (pending execution).
153-
* A backpressure of pending jobs may happen when multiple different callers share the same semaphore
154-
* instance, each being unaware of the others.
155-
* To extend the waiting behavior to include **potentially pending jobs** which account for backpressure,
156-
* use the optional `considerPendingJobsBackpressure` parameter set to `true`. When this flag is enabled,
157-
* the method will account for both existing and future backpressure, even if the backpressure arises
158-
* after the method is invoked.
159-
*
160-
* @param considerPendingJobsBackpressure A boolean indicating whether this method should also wait for
161-
* the resolution of all potentially queued jobs (i.e., those not
162-
* yet executed when the method was invoked).
135+
* A backpressure of pending jobs may happen when multiple different callers share the same
136+
* semaphore instance, each being unaware of the others.
137+
* To extend the waiting behavior to include **potentially pending jobs** which account for
138+
* backpressure, use the optional `considerPendingJobsBackpressure` parameter set to `true`.
139+
* When this flag is enabled, the method will account for both existing and future backpressure,
140+
* even if the backpressure arises after the method is invoked.
141+
*
142+
* @param considerPendingJobsBackpressure A boolean indicating whether this method should also wait
143+
* for the resolution of all potentially queued jobs (i.e.,
144+
* those not yet executed when the method was invoked).
163145
* This is especially relevant when multiple different callers
164-
* share the same semaphore instance, each being unaware of the
165-
* others.
146+
* share the same semaphore instance, each being unaware of
147+
* the others.
166148
* @returns A promise that resolves once all currently executing jobs have completed.
167149
* If `considerPendingJobsBackpressure` is `true`, the promise will additionally
168150
* wait until all queued jobs have been executed, ensuring no pending job backpressure remains.
169151
*/
170152
waitForAllExecutingJobsToComplete(considerPendingJobsBackpressure?: boolean): Promise<void>;
171153
/**
172-
* waitForAvailability
173-
*
174154
* This method resolves once at least one slot is available for job execution.
175155
* In other words, it resolves when the semaphore is available to trigger a new job immediately.
176156
*
@@ -185,19 +165,19 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
185165
* To prevent such potential backpressure, users can utilize the `waitForAvailability` method
186166
* before consuming the next message.
187167
*
188-
* ### Rarely Needed
189-
* This method can be useful when the system is experiencing high load (as indicated by CPU and/or memory
190-
* usage metrics), and you want to pause further async operations until an available job slot opens up.
168+
* ### Design Choice
169+
* This method can be useful when the system is experiencing high load (as indicated by CPU
170+
* and/or memory usage metrics), and you want to pause further async operations until an available
171+
* job slot opens up.
191172
* However, the same effect can be achieved with `startExecution` alone if the async logic
192-
* (intended to be delayed until availability) is handled within the job itself rather than as a preliminary
193-
* step. Therefore, `waitForAvailability` serves as a design choice rather than a strict necessity.
173+
* (intended to be delayed until availability) is handled within the job itself rather than as
174+
* a preliminary step. Therefore, `waitForAvailability` serves as a design choice rather than a
175+
* strict necessity.
194176
*
195177
* @returns A promise that resolves once at least one slot is available.
196178
*/
197179
waitForAvailability(): Promise<void>;
198180
/**
199-
* extractUncaughtErrors
200-
*
201181
* This method returns an array of uncaught errors, captured by the semaphore while executing
202182
* background jobs added by `startExecution`. The term `extract` implies that the semaphore
203183
* instance will no longer hold these error references once extracted, unlike `get`. In other
@@ -217,8 +197,6 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
217197
extractUncaughtErrors(): UncaughtErrorType[];
218198
private _getAvailableSlot;
219199
/**
220-
* _handleJobExecution
221-
*
222200
* This method manages the execution of a given job in a controlled manner. It ensures that
223201
* the job is executed within the constraints of the semaphore and handles updating the
224202
* internal state once the job has completed.
@@ -227,11 +205,11 @@ export declare class ZeroBackpressureSemaphore<T, UncaughtErrorType = Error> {
227205
* - Waits for the job to either return a value or throw an error.
228206
* - Updates the internal state to make the allotted slot available again once the job is finished.
229207
*
230-
* @param job - The job to be executed in the given slot.
231-
* @param allottedSlot - The slot number in which the job should be executed.
232-
* @param isBackgroundJob - A flag indicating whether the caller expects a return value to proceed
233-
* with its work. If `true`, no return value is expected, and any error
234-
* thrown by the job should not be propagated.
208+
* @param job The job to be executed in the given slot.
209+
* @param allottedSlot The slot number in which the job should be executed.
210+
* @param isBackgroundJob A flag indicating whether the caller expects a return value to proceed
211+
* with its work. If `true`, no return value is expected, and any error
212+
* thrown by the job should not be propagated.
235213
* @returns A promise that resolves with the job's return value or rejects with its error.
236214
* Rejection occurs only if triggered by `waitForCompletion`.
237215
*/

0 commit comments

Comments
 (0)