once
once wraps an async function so that it only executes on the first call. Subsequent calls return the cached result. Concurrent calls during the first execution share the same in-flight promise.
Best for one-time initialization Use
oncewhen a resource should be initialized only once, such as database connections, configuration loading, or singleton setup.
Import
Section titled “Import”Root package:
import { once } from "@vgerbot/async";Module subpath:
import { once } from "@vgerbot/async/utils";Leaf subpath:
import { once } from "@vgerbot/async/utils/once";Quick example
Section titled “Quick example”import { once } from "@vgerbot/async";
let callCount = 0;
const initialize = once(async (token) => { callCount++; await token.sleep(100); return { connection: "established" };});
const result1 = await initialize().promise; // callCount = 1const result2 = await initialize().promise; // callCount = 1 (cached)const result3 = await initialize().promise; // callCount = 1 (cached)
console.log(result1 === result2); // true (same object)When to use once
Section titled “When to use once”- Singleton initialization: Ensure a resource is created only once.
- Lazy loading: Defer initialization until first use, then cache.
- Configuration loading: Load configuration once and reuse.
- Connection management: Open a database connection once.
function once<T>( fn: AsyncTask<T>, options?: CancellableOptions<T>,): () => CancellableHandle<T>;Parameters
Section titled “Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
fn |
AsyncTask<T> |
— | The async task to execute once. Receives a CancellableToken. |
options |
CancellableOptions<T> |
undefined |
Cancellable configuration options. |
Return value
Section titled “Return value”Returns a function that returns a CancellableHandle<T>. On the first call, the task is executed. On subsequent calls, a CancellableHandle resolving to the cached result is returned.
Execution model
Section titled “Execution model”- First call: The task is executed via
cancellable. The in-flightCancellableHandleis stored. - Concurrent calls: While the first execution is in progress, concurrent calls return the same in-flight
CancellableHandle. - After completion: The result is cached. All subsequent calls return a new
CancellableHandlethat resolves to the cached value. - Error handling: If the first execution fails, the error is not cached — subsequent calls will retry.
const connect = once(async (token) => { const conn = await openConnection(); return conn;});
// Multiple concurrent calls share the same in-flight promiseconst [conn1, conn2, conn3] = await Promise.all([ connect().promise, connect().promise, connect().promise,]);
console.log(conn1 === conn2); // trueCancellation
Section titled “Cancellation”The first execution’s CancellableHandle can be cancelled. Subsequent calls return a new handle wrapping the cached result (or the in-flight promise if still running).
const init = once(async (token) => { await token.sleep(5000); return "initialized";});
const handle = init();handle.cancel("User cancelled");
// Retry — new execution since the first was cancelledconst result = await init().promise;TypeScript tips
Section titled “TypeScript tips”once is generic over T. The returned function takes no arguments (the task receives only a CancellableToken).
const getConfig = once(async (token) => { const res = await token.wrap(fetch("/api/config")); return res.json() as Promise<{ apiUrl: string }>;});
const config = await getConfig().promise; // { apiUrl: string }Related APIs
Section titled “Related APIs”memoize— caches results by argument.constant— returns a fixed value.cancellable— creates cancellable handles.asyncify— wraps sync functions.