@freemework/common
Version:
Common library of the Freemework Project.
72 lines (61 loc) • 3.1 kB
text/typescript
import { FDisposable } from "./f_disposable.js";
import { FInitable, FInitableBase } from "./f_initable.js";
import { FExecutionContext } from "../execution_context/f_execution_context.js";
import { FExceptionArgument } from "../exception/index.js";
// TODO Add overloading
// return FUsing(
// executionContext,
// () => this.create(executionContext), // <- ! initializer without executionContext
// (db) => worker(db) // <- ! worker without executionContext
// );
export namespace FUsing {
export type ResourceInitializerWithExecutionContext<T> = (executionContext: FExecutionContext) => T | Promise<T>;
export type ResourceInitializerWithoutExecutionContext<T> = () => T | Promise<T>;
export type ResourceInitializer<T> = ResourceInitializerWithExecutionContext<T> | ResourceInitializerWithoutExecutionContext<T>;
export type WorkerWithExecutionContext<TResource, TResult> = (executionContext: FExecutionContext, resource: TResource) => Result<TResult>;
export type WorkerWithoutExecutionContext<TResource, TResult> = (resource: TResource) => Result<TResult>;
export type Worker<TResource, TResult> = WorkerWithExecutionContext<TResource, TResult> | WorkerWithoutExecutionContext<TResource, TResult>;
export type Result<T> = T | Promise<T>;
}
/**
* @deprecated Use TypeScript 5.2 introduces a new keyword - 'using'. See https://github.com/tc39/proposal-explicit-resource-management
*/
export function FUsing<TResource extends FInitable | FDisposable, TResult>(
executionContext: FExecutionContext,
resourceFactory: FUsing.ResourceInitializer<TResource>,
worker: FUsing.Worker<TResource, TResult>
): Promise<TResult> {
if (!resourceFactory || typeof resourceFactory !== "function") {
throw new Error("Wrong argument: resourceFactory");
}
if (!worker) { throw new Error("Wrong argument: worker"); }
async function workerExecutor(workerExecutorCancellationToken: FExecutionContext, disposableResource: TResource): Promise<TResult> {
if (disposableResource instanceof FInitableBase || "init" in disposableResource) {
await (disposableResource as FInitable).init(executionContext);
}
try {
let workerResult: FUsing.Result<TResult>;
if (worker.length === 1) {
workerResult = (worker as FUsing.WorkerWithoutExecutionContext<TResource, TResult>)(disposableResource);
} else if (worker.length === 2) {
workerResult = (worker as FUsing.WorkerWithExecutionContext<TResource, TResult>)(workerExecutorCancellationToken, disposableResource);
} else {
throw new FExceptionArgument("Wrong worker function. Expect a function with 1 or 2 arguments.", "worker");
}
if (workerResult instanceof Promise) {
return await workerResult;
} else {
return workerResult;
}
} finally {
await disposableResource.dispose();
}
}
const resource = resourceFactory(executionContext);
if (resource instanceof Promise) {
return (resource as Promise<TResource>)
.then(disposableInstance => workerExecutor(executionContext, disposableInstance));
} else {
return workerExecutor(executionContext, resource);
}
}