Skip to content

Commit 5fa46fc

Browse files
committed
Bump version to v1.1.0: add shouldExecuteFinalRun flag for final execution on stop
1 parent 904e8bb commit 5fa46fc

10 files changed

+274
-46
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The `NonOverlappingRecurringTask` class provides a modern `setInterval` substitute tailored for asynchronous tasks, ensuring non-overlapping executions by **skipping** attempts if a previous execution is still in progress.
44

5-
Special emphasis is given to **graceful teardown**: The ability to await the completion of an ongoing execution - particularly during application shutdown - makes it ideal for production environments requiring **seamless resource cleanup**.
5+
Special emphasis is given to **graceful teardown**: The ability to await the completion of an ongoing execution - particularly during application shutdown - makes it ideal for production environments requiring seamless resource cleanup.
66

77
## Table of Contents
88

@@ -20,11 +20,12 @@ Special emphasis is given to **graceful teardown**: The ability to await the com
2020
## Key Features :sparkles:<a id="key-features"></a>
2121

2222
* __Guaranteed Non-Overlapping Executions :lock:__: Prevents race conditions ([Race Conditions: How Are They Possible in Single-Threaded JavaScript?](https://www.npmjs.com/package/zero-overhead-promise-lock#race-conditions)) and provides precise control over resource usage. Ideal for batch processing tasks that must run exclusively to manage network bandwidth efficiently.
23-
* __Graceful and Deterministic Teardown :hourglass_flowing_sand:__: When the `stop()` method is invoked during task execution, it resolves only **after** the execution is complete. 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.
23+
* __Graceful and Deterministic Teardown :hourglass_flowing_sand:__: When the `stop` method is invoked during task execution, it resolves only **after** the execution is complete. 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.
2424
* __Fixed Delay Between Executions :repeat:__: Functions similarly to JavaScript's built-in `setInterval`, but skips executions if a previous one is still in progress.
25-
- __Flexible First Execution Policy :level_slider:__: The `immediateFirstRun` option lets you control whether execution begins immediately upon `start()` or only after the first interval. Particularly useful when the task is part of an **application’s bootstrap phase** (e.g., `onModuleInit()` in NestJS). If the bootstrap phase requires the first execution to complete before proceeding (e.g., before accepting HTTP requests), pair this with `waitUntilCurrentExecutionCompletes()`.
25+
- __Flexible First Execution Policy :level_slider:__: The `immediateFirstRun` option lets you control whether execution begins immediately upon `start` or only after the first interval. Particularly useful when the task is part of an **application’s bootstrap phase** (e.g., `onModuleInit` in NestJS). If the bootstrap phase requires the first execution to complete before proceeding (e.g., before accepting HTTP requests), pair this with `waitUntilCurrentExecutionCompletes`.
26+
- __Optional Final Digest Run :broom:__: The optional `shouldExecuteFinalRun` flag allows a final execution to be performed as part of the `stop` process. This is especially useful for tasks that accumulate state between executions and need a final flush to persistent storage to avoid leaving unprocessed data. Examples include delayed publishing of batched Kafka messages and upserting accumulated data into a database.
2627
- __Error Handling :warning:__: If a periodic task throws an error, it is passed to an optional error handler callback, if provided. This component does **not** perform any logging, as it is designed to be **agnostic of user preferences**, such as specific loggers or logging styles. A typical `_onTaskError` implementation logs errors based on the user's logging strategy. If the periodic task already handles its own errors, this handler can be omitted.
27-
- __Execution State Metrics :bar_chart:__: The `status` and `isCurrentlyExecuting` getters offer real-time insights into the scheduler's state, helping users make informed decisions, such as awaiting `waitUntilCurrentExecutionCompletes()` when specific operations must not overlap the recurring task.
28+
- __Execution State Metrics :bar_chart:__: The `status` and `isCurrentlyExecuting` getters offer real-time insights into the scheduler's state, helping users make informed decisions, such as awaiting `waitUntilCurrentExecutionCompletes` when specific operations must not overlap the recurring task.
2829
- __Comprehensive documentation :books:__: Fully documented, enabling IDEs to provide intelligent **tooltips** for an enhanced development experience.
2930
- __Thoroughly Tested :test_tube:__: Backed by extensive unit tests, covering even rare edge cases, to ensure reliability in production.
3031
- __Zero Runtime Dependencies :dove:__: Only development dependencies are included.

dist/non-overlapping-recurring-task.d.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ export declare class NonOverlappingRecurringTask<UncaughtErrorType = Error> {
181181
* ### Graceful Teardown
182182
* If this method is invoked during an ongoing execution, it resolves only after the
183183
* current execution completes. This guarantee ensures **determinism** and allows for
184-
* a **graceful teardown**.
184+
* a **graceful teardown**. If the `shouldExecuteFinalRun` flag is enabled, the method
185+
* also waits for the final (digest) run to complete.
185186
* Use cases where it is essential to complete any ongoing execution before proceeding include:
186187
* - **Application Shutdowns**: In cases like `onModuleDestroy` in NestJS applications, where
187188
* tasks should complete before termination. For instance, ensuring a bulk-write to a database
@@ -195,12 +196,32 @@ export declare class NonOverlappingRecurringTask<UncaughtErrorType = Error> {
195196
* In case the instance is in a termination status (i.e., awaiting completion of the last execution),
196197
* a redundant call will wait for the ongoing execution to complete before resolving.
197198
*
199+
* ### Optional: Force an Additional Final Run
200+
* Enabling the `shouldExecuteFinalRun` flag triggers **one final execution** before resolving.
201+
* This is particularly useful for tasks that accumulate state between executions and require
202+
* a final flush (write operation) to **ensure no unprocessed data remains**.
203+
* #### When This is Relevant
204+
* - Flushing Batched Writes: A log aggregator that periodically writes accumulated logs to a
205+
* database should execute a final flush before stopping to prevent data loss.
206+
* - Committing Transactions: A system that batches updates should perform one last batch
207+
* before stopping to ensure all changes are committed.
208+
* #### When This is Less Relevant
209+
* - Periodic Data Fetches: If the task refreshes external configurations at regular intervals,
210+
* such as feature flags, an additional fetch before stopping provides no meaningful benefit.
211+
*
212+
* @param shouldExecuteFinalRun - If `true`, ensures that one final execution occurs as part of the
213+
* stop process. This is particularly useful for tasks that **accumulate
214+
* state between executions** and require a final flush to avoid leaving
215+
* unprocessed data. To eliminate any ambiguity, when this flag is enabled,
216+
* the `stop` method resolves only **after** the final execution completes.
198217
* @returns `true` if recurring executions were stopped by this invocation (i.e., the instance's
199-
* status changed from active to inactive);
218+
* status changed from 'active' to 'inactive');
200219
* `false` if the instance was already inactive or in termination status, and the
201-
* invocation had no effect.
220+
* invocation had no effect. Note that even when `false` is returned, the call
221+
* still waits for the last run to complete if invoked while the instance is in a
222+
* 'terminating' status.
202223
*/
203-
stop(): Promise<boolean>;
224+
stop(shouldExecuteFinalRun?: boolean): Promise<boolean>;
204225
/**
205226
* Executes the task in a controlled manner:
206227
* - Triggers the optional error handler (if provided) when the task rejects with an error.

dist/non-overlapping-recurring-task.js

Lines changed: 39 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/non-overlapping-recurring-task.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)