Skip to content

Commit

Permalink
Bump version to v2.0.1: Make error type generic for flexibility, impr…
Browse files Browse the repository at this point in the history
…ove docs, and prefer undefined over null
  • Loading branch information
ori88c committed Sep 19, 2024
1 parent 06e7a86 commit 38db4cd
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 37 deletions.
24 changes: 20 additions & 4 deletions dist/non-overlapping-periodic-job-scheduler.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { PeriodicJob, CalculateDelayTillNextExecution } from './types';
* This class is fully covered by unit tests.
*
*/
export declare class NonOverlappingPeriodicJobScheduler {
export declare class NonOverlappingPeriodicJobScheduler<JobError = Error> {
private readonly _periodicJob;
private readonly _calculateDelayTillNextExecution;
private _isStopped;
Expand All @@ -55,18 +55,34 @@ export declare class NonOverlappingPeriodicJobScheduler {
/**
* constructor
*
* @param _periodicJob the periodic job
* @param _calculateDelayTillNextExecution check full documentation at types.ts
* @param _periodicJob A periodic job.
* @param _calculateDelayTillNextExecution Function to calculate the delay until the
* next execution, based on the duration and
* any error thrown by the previous execution.
*/
constructor(_periodicJob: PeriodicJob, _calculateDelayTillNextExecution: CalculateDelayTillNextExecution);
constructor(_periodicJob: PeriodicJob, _calculateDelayTillNextExecution: CalculateDelayTillNextExecution<JobError>);
get isCurrentlyExecuting(): boolean;
/**
* isStopped
*
* Indicates whether the instance is currently *not* managing periodic executions.
*
* @returns `true` if the instance has no periodic executions currently scheduled
* or in progress, otherwise `false`.
*/
get isStopped(): boolean;
/**
* start
*
* Initiates the scheduling of periodic jobs.
*/
start(): void;
/**
* waitTillCurrentExecutionSettles
*
* Resolves when the current execution completes, if called during an ongoing execution.
* If no execution is in progress, it resolves immediately.
*/
waitTillCurrentExecutionSettles(): Promise<void>;
/**
* stop
Expand Down
38 changes: 28 additions & 10 deletions dist/non-overlapping-periodic-job-scheduler.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/non-overlapping-periodic-job-scheduler.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ export declare const NO_PREVIOUS_EXECUTION = -1;
* variety of use cases.
*
*/
export type CalculateDelayTillNextExecution = (justFinishedExecutionDurationMs: number, justFinishedExecutionError?: Error) => number;
export type CalculateDelayTillNextExecution<JobError = Error> = (justFinishedExecutionDurationMs: number, justFinishedExecutionError?: JobError) => number;
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "non-overlapping-periodic-job-scheduler",
"version": "2.0.0",
"version": "2.0.1",
"description": "Periodic job scheduler ensuring non-overlapping executions and deterministic termination. Features a dynamically chosen interval between executions, determined by a user-injected calculator function.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -29,7 +29,8 @@
"scheduler",
"interval",
"time-interval",
"dynamic",
"dynamic-interval",
"flexible-interval",
"executor",
"executions",
"task-manager",
Expand Down
55 changes: 40 additions & 15 deletions src/non-overlapping-periodic-job-scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ import { PeriodicJob, CalculateDelayTillNextExecution, NO_PREVIOUS_EXECUTION } f
* This class is fully covered by unit tests.
*
*/
export class NonOverlappingPeriodicJobScheduler {
export class NonOverlappingPeriodicJobScheduler<JobError = Error> {
private _isStopped: boolean = true;
private _nextExecutionTimer: NodeJS.Timeout | null = null;
private _currentExecutionPromise: Promise<void> | null = null;
private _nextExecutionTimer: NodeJS.Timeout = undefined;
private _currentExecutionPromise: Promise<void> = undefined;

// The `setTimeout` callback is deliberately non-async, to prevent dangling promises.
// Such are undesired, as they cannot be awaited, which is crucial for a deterministic
Expand All @@ -61,18 +61,28 @@ export class NonOverlappingPeriodicJobScheduler {
/**
* constructor
*
* @param _periodicJob the periodic job
* @param _calculateDelayTillNextExecution check full documentation at types.ts
* @param _periodicJob A periodic job.
* @param _calculateDelayTillNextExecution Function to calculate the delay until the
* next execution, based on the duration and
* any error thrown by the previous execution.
*/
constructor(
private readonly _periodicJob: PeriodicJob,
private readonly _calculateDelayTillNextExecution: CalculateDelayTillNextExecution,
private readonly _calculateDelayTillNextExecution: CalculateDelayTillNextExecution<JobError>,
) { }

public get isCurrentlyExecuting(): boolean {
return this._currentExecutionPromise !== null;
return this._currentExecutionPromise !== undefined;
}

/**
* isStopped
*
* Indicates whether the instance is currently *not* managing periodic executions.
*
* @returns `true` if the instance has no periodic executions currently scheduled
* or in progress, otherwise `false`.
*/
public get isStopped(): boolean {
return this._isStopped;
}
Expand All @@ -92,6 +102,12 @@ export class NonOverlappingPeriodicJobScheduler {
this._nextExecutionTimer = setTimeout(this._triggerExecution, firstExecutionDelay);
}

/**
* waitTillCurrentExecutionSettles
*
* Resolves when the current execution completes, if called during an ongoing execution.
* If no execution is in progress, it resolves immediately.
*/
public waitTillCurrentExecutionSettles(): Promise<void> {
return this._currentExecutionPromise ?? Promise.resolve();
}
Expand All @@ -108,15 +124,15 @@ export class NonOverlappingPeriodicJobScheduler {

if (this._nextExecutionTimer) {
clearTimeout(this._nextExecutionTimer);
this._nextExecutionTimer = null;
this._nextExecutionTimer = undefined;
}

return this.waitTillCurrentExecutionSettles();
}

private async _triggerCurrentExecutionAndScheduleNext(): Promise<void> {
this._nextExecutionTimer = null;
let thrownError: Error | undefined = undefined;
this._nextExecutionTimer = undefined;
let thrownError: JobError = undefined;

const startTime = Date.now();
try {
Expand All @@ -125,19 +141,28 @@ export class NonOverlappingPeriodicJobScheduler {
thrownError = err;
}

this._currentExecutionPromise = null;
this._currentExecutionPromise = undefined;
if (this._isStopped) {
return;
}

const justFinishedExecutionDurationMs = Date.now() - startTime;
try {
const delayTillNextExecution = this._calculateDelayTillNextExecution(justFinishedExecutionDurationMs, thrownError);
this._nextExecutionTimer = setTimeout(this._triggerExecution, delayTillNextExecution);
const delayTillNextExecution = this._calculateDelayTillNextExecution(
justFinishedExecutionDurationMs,
thrownError
);
this._nextExecutionTimer = setTimeout(
this._triggerExecution,
delayTillNextExecution
);
} catch (err) {
// The calculator should never throw an error, so this scenario is unlikely.
// However, we handle it to ensure robustness.
// The calculator should never throw an error.
// However, we handle this scenario to maintain robustness.
this._isStopped = true;

// Propagating an error back to the calling stack can be critical and may crash
// the application. Given the severity, this behavior is appropriate.
throw err;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const NO_PREVIOUS_EXECUTION = -1;
* variety of use cases.
*
*/
export type CalculateDelayTillNextExecution = (
export type CalculateDelayTillNextExecution<JobError = Error> = (
justFinishedExecutionDurationMs: number,
justFinishedExecutionError?: Error
justFinishedExecutionError?: JobError
) => number;

0 comments on commit 38db4cd

Please sign in to comment.