BREAKING CHANGE(taskbuffer): Change default Task error handling: trigger() now rejects when taskFunction throws; add catchErrors option (default false) to preserve previous swallow behavior; track errors (lastError, errorCount) and expose them in metadata; improve error propagation and logging across runners, chains, parallels and debounced tasks; add tests and documentation for new behavior.
This commit is contained in:
@@ -87,24 +87,37 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
taskToRun.running = true;
|
||||
taskToRun.runCount++;
|
||||
taskToRun.lastRun = new Date();
|
||||
|
||||
// Reset steps at the beginning of task execution
|
||||
|
||||
// Reset steps and error state at the beginning of task execution
|
||||
taskToRun.resetSteps();
|
||||
taskToRun.lastError = undefined;
|
||||
|
||||
done.promise.then(async () => {
|
||||
taskToRun.running = false;
|
||||
|
||||
// Complete all steps when task finishes
|
||||
taskToRun.completeAllSteps();
|
||||
done.promise
|
||||
.then(async () => {
|
||||
taskToRun.running = false;
|
||||
|
||||
// When the task has finished running, resolve the finished promise
|
||||
taskToRun.resolveFinished();
|
||||
// Complete all steps when task finishes
|
||||
taskToRun.completeAllSteps();
|
||||
|
||||
// Create a new finished promise for the next run
|
||||
taskToRun.finished = new Promise((resolve) => {
|
||||
taskToRun.resolveFinished = resolve;
|
||||
// When the task has finished running, resolve the finished promise
|
||||
taskToRun.resolveFinished();
|
||||
|
||||
// Create a new finished promise for the next run
|
||||
taskToRun.finished = new Promise((resolve) => {
|
||||
taskToRun.resolveFinished = resolve;
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
taskToRun.running = false;
|
||||
|
||||
// Resolve finished so blocking dependants don't hang
|
||||
taskToRun.resolveFinished();
|
||||
|
||||
// Create a new finished promise for the next run
|
||||
taskToRun.finished = new Promise((resolve) => {
|
||||
taskToRun.resolveFinished = resolve;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const options = {
|
||||
...{ x: undefined, touchedTasksArray: [] },
|
||||
@@ -133,7 +146,13 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
try {
|
||||
return await taskToRun.taskFunction(x, taskToRun.setupValue);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
taskToRun.lastError = e instanceof Error ? e : new Error(String(e));
|
||||
taskToRun.errorCount++;
|
||||
logger.log('error', `Task "${taskToRun.name || 'unnamed'}" failed: ${taskToRun.lastError.message}`);
|
||||
if (taskToRun.catchErrors) {
|
||||
return undefined;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.then((x) => {
|
||||
@@ -155,10 +174,18 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
done.resolve(x);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
done.reject(err);
|
||||
});
|
||||
localDeferred.resolve();
|
||||
return await done.promise;
|
||||
|
||||
try {
|
||||
return await done.promise;
|
||||
} catch (err) {
|
||||
if (taskToRun.catchErrors) {
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
public name: string;
|
||||
@@ -187,10 +214,19 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
public lastRun?: Date;
|
||||
public runCount: number = 0;
|
||||
|
||||
// Error handling
|
||||
public catchErrors: boolean = false;
|
||||
public lastError?: Error;
|
||||
public errorCount: number = 0;
|
||||
|
||||
public get idle() {
|
||||
return !this.running;
|
||||
}
|
||||
|
||||
public clearError(): void {
|
||||
this.lastError = undefined;
|
||||
}
|
||||
|
||||
public taskSetup: ITaskSetupFunction<T>;
|
||||
public setupValue: T;
|
||||
|
||||
@@ -210,6 +246,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
name?: string;
|
||||
taskSetup?: ITaskSetupFunction<T>;
|
||||
steps?: TSteps;
|
||||
catchErrors?: boolean;
|
||||
}) {
|
||||
this.taskFunction = optionsArg.taskFunction;
|
||||
this.preTask = optionsArg.preTask;
|
||||
@@ -219,6 +256,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
this.execDelay = optionsArg.execDelay;
|
||||
this.name = optionsArg.name;
|
||||
this.taskSetup = optionsArg.taskSetup;
|
||||
this.catchErrors = optionsArg.catchErrors ?? false;
|
||||
|
||||
// Initialize steps if provided
|
||||
if (optionsArg.steps) {
|
||||
@@ -306,10 +344,21 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
|
||||
// Get task metadata
|
||||
public getMetadata(): ITaskMetadata {
|
||||
let status: 'idle' | 'running' | 'completed' | 'failed';
|
||||
if (this.running) {
|
||||
status = 'running';
|
||||
} else if (this.lastError) {
|
||||
status = 'failed';
|
||||
} else if (this.runCount > 0) {
|
||||
status = 'completed';
|
||||
} else {
|
||||
status = 'idle';
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name || 'unnamed',
|
||||
version: this.version,
|
||||
status: this.running ? 'running' : 'idle',
|
||||
status,
|
||||
steps: this.getStepsMetadata(),
|
||||
currentStep: this.currentStepName,
|
||||
currentProgress: this.getProgress(),
|
||||
@@ -318,6 +367,8 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
bufferMax: this.bufferMax,
|
||||
timeout: this.timeout,
|
||||
cronSchedule: this.cronJob?.cronExpression,
|
||||
lastError: this.lastError?.message,
|
||||
errorCount: this.errorCount,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user