Lazy
import { Lazy } from "jsr:@ende/async";class
class Lazy<T>A lazy value that is initialized at most once, with built-in deduplication of
concurrent callers. Prevents the common race where two concurrent get() calls
both trigger the initializer; only one initialization runs and all callers share
the same promise.
If the initializer rejects, the error is propagated to all concurrent callers
and the internal state is cleared — the next Lazy.prototype.get
call will re-run the initializer. Compose with retry for
automatic back-off on transient failures.
Example: Concurrent deduplication
import { Lazy } from "@ende/async/lazy";
import { assertEquals } from "@ende/assert";
let initCount = 0;
const config = new Lazy<number>(async () => {
initCount++;
return 42;
});
const [a, b] = await Promise.all([config.get(), config.get()]);
assertEquals(a, 42);
assertEquals(b, 42);
assertEquals(initCount, 1);
Example: Composing with retry
import { Lazy } from "@ende/async/lazy";
import { retry } from "@ende/async/retry";
const db = new Lazy(() =>
retry(() => connectDb(), { minTimeout: 100, maxAttempts: 3 })
);
await db.get();
Constructors
constructor
new Lazy(init: () => T | Promise<T>)Creates a new lazy value.
init: () => T | Promise<T>Initializer function, called at most once (until
reset).
Methods
get
get(options?: LazyGetOptions): Promise<T>Returns the cached value, initializing it on first call. Concurrent callers share the same in-flight promise — the initializer is never invoked more than once at a time.
Always returns a promise, even when the initializer is synchronous.
options?: LazyGetOptionsOptional settings for this call.
Returns
The cached or newly initialized value.
Example: Usage
import { Lazy } from "@ende/async/lazy";
const config = new Lazy(async () => ({ loaded: true }));
const value = await config.get();
Example: Abort a slow initialization
import { Lazy } from "@ende/async/lazy";
import { assertRejects } from "@ende/assert";
const slow = new Lazy(() => new Promise<string>(() => {}));
const controller = new AbortController();
controller.abort(new Error("timed out"));
await assertRejects(
() => slow.get({ signal: controller.signal }),
Error,
"timed out",
);
initialized
get initialized(): booleanWhether the value has been successfully initialized.
Returns
true if the value has been initialized, false otherwise.
Example: Check initialization state
import { Lazy } from "@ende/async/lazy";
import { assertEquals } from "@ende/assert";
const lazy = new Lazy(() => 42);
assertEquals(lazy.initialized, false);
await lazy.get();
assertEquals(lazy.initialized, true);
peek
peek(): { ok: true; value: T } | { ok: false }Returns the value if already resolved, or indicates that it is not yet
available. The discriminated union avoids ambiguity when T itself can
be undefined.
Returns
{ ok: true, value } if the value has been initialized, or
{ ok: false } if not yet initialized or still in-flight.
Example: Fast-path when already initialized
import { Lazy } from "@ende/async/lazy";
import { assertEquals } from "@ende/assert";
const config = new Lazy(async () => ({ port: 8080 }));
await config.get();
const result = config.peek();
assertEquals(result, { ok: true, value: { port: 8080 } });
Example: Not yet initialized
import { Lazy } from "@ende/async/lazy";
import { assertEquals } from "@ende/assert";
const lazy = new Lazy(() => 42);
assertEquals(lazy.peek(), { ok: false });
reset
reset(): voidResets the lazy so the next get re-runs the initializer. Does
not cancel an in-flight initialization; callers that already have the
promise will still receive its result.
Example: Force reload
import { Lazy } from "@ende/async/lazy";
const config = new Lazy(async () => loadConfig());
await config.get();
config.reset();
const fresh = await config.get();