with-defer
Version:
Run a function with an injected go-like defer helper
74 lines (71 loc) • 2.95 kB
TypeScript
/**
* CleanupCallback functions will be automatically called when the handler's returned promise
* settles.
*
* These can be used to perform some resource clean-up and will be completed before yielding
* control back to the caller.
*/
interface CleanupCallback {
(): any;
}
/**
* DeferFunction is a function that can be used to register clean-up functions to be called
* before control is yielded back to the caller.
*/
interface DeferFunction {
(deferredFn: CleanupCallback): void;
}
/**
* FunctionWithCleanup is a function that will be run with an injected `defer` argument.
*/
interface FunctionWithCleanup {
(defer: DeferFunction): any;
}
declare type ReturnedPromise<TFunc extends FunctionWithCleanup> = Promise<ReturnType<TFunc> extends PromiseLike<infer U> ? U : ReturnType<TFunc>>;
/**
* Run a handler function (`func`) such that any callbacks registered with the `defer` argument
* passed to it will be called in LIFO order when the function completes.
*
* This is conceptually similar to the `defer` statement in Go in that you can use it to register
* resources for disposal as they are created. Each deferred callback will be await in LIFO order.
* Any errors thrown while calling these deferred callbacks will be collected without preventing
* other callback from executing. If any errors are thrown an
* [`AggregateError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError)
* will be thrown with these errors.
*
* @param func Function that will be called with a single `defer` argument that is itself a function that an be called to register callbacks that will be called in LIFO whenever the handler completes.
* @returns A `Promise` whose settled value will match the result of executing the handler `func`.
*
* Example:
*
* ```ts
* import * as Fs from "fs";
* import { runWithDefer } from "with-defer";
*
* async function main() {
* await runWithDefer(async (defer) => {
* // Open a file hande and make sure we close it.
* const handle1 = await Fs.promises.open("path/to/file", "r+");
* defer(() => handle1.close());
*
* const content = await handle1.readFile("utf8");
*
* // We open a 2nd handle here and register its close function. Notice we don't have to deal
* // with crazy nesting of try / catch blocks and can co-locate clean-up with obtaining the
* // resource.
* const handle2 = await Fs.promises.open("path/to/file2", "w+");
* defer(() => handle2.close());
*
* await handle2.writeFile(content);
* });
* }
* ```
*/
declare function runWithDefer<TFunc extends FunctionWithCleanup>(func: TFunc): ReturnedPromise<TFunc>;
declare class AggregateError extends Error {
readonly message: string;
readonly errors: unknown[];
readonly name = "AggregateError";
constructor(errors: Iterable<unknown>, message?: string);
}
export { AggregateError, runWithDefer };