Concurrent task execution with cancellation support, promise utilities, and error aggregation for managing complex asynchronous workflows. Provides utilities for task coordination, error handling, and promise-based operations.
Manages concurrent execution of multiple asynchronous tasks with cancellation support.
/**
* Manages concurrent asynchronous tasks with cancellation support
*/
class AsyncTaskManager {
/** Array of managed task promises */
readonly tasks: Array<Promise<any>>;
/** Array of accumulated errors from failed tasks */
private readonly errors: Array<Error>;
/**
* Create task manager with cancellation token
* @param cancellationToken - Token for task cancellation
*/
constructor(cancellationToken: CancellationToken);
/**
* Add a task function to be executed
* @param task - Function returning a promise
*/
add(task: () => Promise<any>): void;
/**
* Add an already-created promise to be managed
* @param promise - Promise to manage
*/
addTask(promise: Promise<any>): void;
/**
* Cancel all managed tasks
*/
cancelTasks(): void;
/**
* Wait for all tasks to complete and return results
* @returns Promise resolving to array of task results
* @throws NestedError if any tasks failed
*/
awaitTasks(): Promise<Array<any>>;
}Usage Examples:
import { AsyncTaskManager } from "builder-util";
// Create task manager
const cancellationToken = new CancellationToken();
const taskManager = new AsyncTaskManager(cancellationToken);
// Add task functions
taskManager.add(async () => {
return await compileTypeScript();
});
taskManager.add(async () => {
return await bundleAssets();
});
taskManager.add(async () => {
return await optimizeImages();
});
// Add existing promises
const existingTask = processData();
taskManager.addTask(existingTask);
try {
// Wait for all tasks to complete
const results = await taskManager.awaitTasks();
console.log("All tasks completed:", results);
} catch (error) {
console.error("Some tasks failed:", error);
// Cancel remaining tasks if needed
taskManager.cancelTasks();
}Utility functions for enhanced promise handling and error management.
/**
* Execute a promise with a guaranteed finally block
* @param promise - Promise to execute
* @param task - Function to run in finally block, receives error status
* @returns Promise resolving to the original promise result
*/
function executeFinally<T>(
promise: Promise<T>,
task: (isErrorOccurred: boolean) => Promise<any>
): Promise<T>;
/**
* Return null if promise fails due to file not existing
* @param promise - Promise that might fail with ENOENT
* @returns Promise resolving to result or null if file doesn't exist
*/
function orNullIfFileNotExist<T>(promise: Promise<T>): Promise<T | null>;
/**
* Return fallback value if promise fails due to file not existing
* @param promise - Promise that might fail with ENOENT
* @param fallbackValue - Value to return if file doesn't exist
* @returns Promise resolving to result or fallback value
*/
function orIfFileNotExist<T>(promise: Promise<T>, fallbackValue: T): Promise<T>;Usage Examples:
import { executeFinally, orNullIfFileNotExist, orIfFileNotExist } from "builder-util";
// Execute with guaranteed cleanup
const result = await executeFinally(
processLargeFile("/path/to/file.dat"),
async (isErrorOccurred) => {
// Cleanup always runs, regardless of success/failure
await cleanupTempFiles();
if (isErrorOccurred) {
console.log("Processing failed, cleanup completed");
} else {
console.log("Processing succeeded, cleanup completed");
}
}
);
// Handle optional file operations
const config = await orNullIfFileNotExist(
readFile("/optional/config.json")
);
if (config) {
console.log("Config loaded:", config);
} else {
console.log("No config file found, using defaults");
}
// Handle with fallback value
const settings = await orIfFileNotExist(
readSettingsFile("/user/settings.json"),
getDefaultSettings()
);Specialized error class for handling multiple related errors.
/**
* Error class that aggregates multiple errors into a single error
*/
class NestedError extends Error {
/**
* Create error from array of errors
* @param errors - Array of errors to aggregate
* @param message - Optional custom message prefix
*/
constructor(errors: Array<Error>, message?: string);
}Usage Examples:
import { NestedError } from "builder-util";
// Collect errors from multiple operations
const errors: Array<Error> = [];
try {
await operation1();
} catch (error) {
errors.push(error);
}
try {
await operation2();
} catch (error) {
errors.push(error);
}
try {
await operation3();
} catch (error) {
errors.push(error);
}
// Throw aggregated error if any failed
if (errors.length > 0) {
throw new NestedError(errors, "Multiple operations failed:");
}Utility for handling fatal errors and process termination.
/**
* Print error and exit process with code 1
* @param error - Error to print before exiting
*/
function printErrorAndExit(error: Error): void;Usage Examples:
import { printErrorAndExit } from "builder-util";
// Handle fatal errors
process.on("unhandledRejection", (error: Error) => {
console.error("Unhandled promise rejection:");
printErrorAndExit(error);
});
// Use in CLI applications
try {
await runBuildProcess();
} catch (error) {
printErrorAndExit(error);
}import { AsyncTaskManager } from "builder-util";
class BuildPipeline {
async build(targets: Array<BuildTarget>) {
const cancellationToken = new CancellationToken();
const taskManager = new AsyncTaskManager(cancellationToken);
// Add build tasks for each target
for (const target of targets) {
taskManager.add(async () => {
console.log(`Building for ${target.platform}-${target.arch}`);
return await this.buildForTarget(target);
});
}
// Add asset processing in parallel
taskManager.add(async () => {
return await this.processAssets();
});
taskManager.add(async () => {
return await this.generateManifests();
});
try {
const results = await taskManager.awaitTasks();
console.log("All build tasks completed");
return results;
} catch (error) {
console.error("Build failed:", error);
taskManager.cancelTasks();
throw error;
}
}
}import { executeFinally } from "builder-util";
class ResourceManager {
async processWithResources<T>(operation: () => Promise<T>): Promise<T> {
const resources = await this.acquireResources();
return executeFinally(
operation(),
async (isErrorOccurred) => {
await this.releaseResources(resources);
if (isErrorOccurred) {
await this.cleanupFailedOperation();
}
}
);
}
private async acquireResources() {
// Acquire database connections, file handles, etc.
return {
dbConnection: await connectToDatabase(),
tempDir: await createTempDirectory()
};
}
private async releaseResources(resources: any) {
// Always cleanup resources
await resources.dbConnection?.close();
await removeTempDirectory(resources.tempDir);
}
}import { orNullIfFileNotExist, orIfFileNotExist } from "builder-util";
class ConfigManager {
async loadConfiguration(): Promise<AppConfig> {
// Try loading user config, fall back to defaults
const userConfig = await orNullIfFileNotExist(
this.loadUserConfig()
);
const projectConfig = await orIfFileNotExist(
this.loadProjectConfig(),
this.getDefaultProjectConfig()
);
// Merge configurations
return {
...this.getDefaultConfig(),
...projectConfig,
...userConfig
};
}
private async loadUserConfig() {
const configPath = path.join(os.homedir(), ".myapp", "config.json");
return JSON.parse(await fs.readFile(configPath, "utf8"));
}
private async loadProjectConfig() {
return JSON.parse(await fs.readFile("./myapp.config.json", "utf8"));
}
}import { NestedError } from "builder-util";
class ValidationEngine {
async validateProject(projectPath: string): Promise<ValidationResult> {
const errors: Array<Error> = [];
const warnings: Array<string> = [];
// Collect all validation errors
try {
await this.validatePackageJson(projectPath);
} catch (error) {
errors.push(error);
}
try {
await this.validateTypeScript(projectPath);
} catch (error) {
errors.push(error);
}
try {
await this.validateAssets(projectPath);
} catch (error) {
errors.push(error);
}
// Return results or throw aggregated error
if (errors.length > 0) {
throw new NestedError(errors, "Project validation failed:");
}
return {
isValid: true,
warnings
};
}
}