diff --git a/async/deno.json b/async/deno.json index dc84df585695..19a68fe56222 100644 --- a/async/deno.json +++ b/async/deno.json @@ -20,7 +20,7 @@ "./unstable-wait-for": "./unstable_wait_for.ts", "./unstable-semaphore": "./unstable_semaphore.ts", "./unstable-circuit-breaker": "./unstable_circuit_breaker.ts", - "./unstable-lazy": "./unstable_lazy.ts", + "./lazy": "./lazy.ts", "./unstable-pool-settled": "./unstable_pool_settled.ts", "./unstable-channel": "./unstable_channel.ts" } diff --git a/async/unstable_lazy.ts b/async/lazy.ts similarity index 87% rename from async/unstable_lazy.ts rename to async/lazy.ts index f9852b60b6b6..fb63a5f5c992 100644 --- a/async/unstable_lazy.ts +++ b/async/lazy.ts @@ -3,8 +3,6 @@ /** * Options for {@linkcode Lazy.prototype.get}. - * - * @experimental **UNSTABLE**: New API, yet to be vetted. */ export interface LazyGetOptions { /** @@ -31,7 +29,7 @@ export interface LazyGetOptions { * @example Concurrent deduplication * * ```ts - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * import { assertEquals } from "@std/assert"; * * let initCount = 0; @@ -49,7 +47,7 @@ export interface LazyGetOptions { * @example Composing with retry * * ```ts ignore - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * import { retry } from "@std/async/retry"; * * const db = new Lazy(() => @@ -58,8 +56,6 @@ export interface LazyGetOptions { * await db.get(); * ``` * - * @experimental **UNSTABLE**: New API, yet to be vetted. - * * @typeParam T The type of the lazily initialized value. */ export class Lazy { @@ -71,8 +67,6 @@ export class Lazy { /** * Creates a new lazy value. * - * @experimental **UNSTABLE**: New API, yet to be vetted. - * * @param init Initializer function, called at most once (until {@linkcode reset}). */ constructor(init: () => T | Promise) { @@ -88,7 +82,7 @@ export class Lazy { * * @example Usage * ```ts no-assert - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * * const config = new Lazy(async () => ({ loaded: true })); * const value = await config.get(); @@ -96,7 +90,7 @@ export class Lazy { * * @example Abort a slow initialization * ```ts - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * import { assertRejects } from "@std/assert"; * * const slow = new Lazy(() => new Promise(() => {})); @@ -109,8 +103,6 @@ export class Lazy { * ); * ``` * - * @experimental **UNSTABLE**: New API, yet to be vetted. - * * @param options Optional settings for this call. * @returns The cached or newly initialized value. */ @@ -163,7 +155,7 @@ export class Lazy { * * @example Check initialization state * ```ts - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * import { assertEquals } from "@std/assert"; * * const lazy = new Lazy(() => 42); @@ -172,8 +164,6 @@ export class Lazy { * assertEquals(lazy.initialized, true); * ``` * - * @experimental **UNSTABLE**: New API, yet to be vetted. - * * @returns `true` if the value has been initialized, `false` otherwise. */ get initialized(): boolean { @@ -187,7 +177,7 @@ export class Lazy { * * @example Fast-path when already initialized * ```ts - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * import { assertEquals } from "@std/assert"; * * const config = new Lazy(async () => ({ port: 8080 })); @@ -199,15 +189,13 @@ export class Lazy { * * @example Not yet initialized * ```ts - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * import { assertEquals } from "@std/assert"; * * const lazy = new Lazy(() => 42); * assertEquals(lazy.peek(), { ok: false }); * ``` * - * @experimental **UNSTABLE**: New API, yet to be vetted. - * * @returns `{ ok: true, value }` if the value has been initialized, or * `{ ok: false }` if not yet initialized or still in-flight. */ @@ -224,15 +212,13 @@ export class Lazy { * * @example Force reload * ```ts ignore - * import { Lazy } from "@std/async/unstable-lazy"; + * import { Lazy } from "@std/async/lazy"; * * const config = new Lazy(async () => loadConfig()); * await config.get(); * config.reset(); * const fresh = await config.get(); * ``` - * - * @experimental **UNSTABLE**: New API, yet to be vetted. */ reset(): void { this.#promise = undefined; diff --git a/async/unstable_lazy_test.ts b/async/lazy_test.ts similarity index 90% rename from async/unstable_lazy_test.ts rename to async/lazy_test.ts index 5db6fb7de2c9..33e60d713445 100644 --- a/async/unstable_lazy_test.ts +++ b/async/lazy_test.ts @@ -1,7 +1,7 @@ // Copyright 2018-2026 the Deno authors. MIT license. import { assertEquals, assertRejects, assertStrictEquals } from "@std/assert"; -import { Lazy } from "./unstable_lazy.ts"; +import { Lazy } from "./lazy.ts"; Deno.test("Lazy.get() initializes and returns sync value", async () => { const lazy = new Lazy(() => 42); @@ -181,6 +181,35 @@ Deno.test("Lazy.reset() does not affect in-flight initialization", async () => { assertEquals(value, "ok"); }); +Deno.test("Lazy.get() after reset() during in-flight triggers fresh init", async () => { + let initCount = 0; + const holders: { resolve: (v: number) => void }[] = []; + const lazy = new Lazy( + () => + new Promise((res) => { + initCount++; + holders.push({ resolve: res }); + }), + ); + + const first = lazy.get(); + await Promise.resolve(); + assertEquals(initCount, 1); + + lazy.reset(); + const second = lazy.get(); + await Promise.resolve(); + assertEquals(initCount, 2); + + holders[0]!.resolve(1); + holders[1]!.resolve(2); + + assertEquals(await first, 1); + assertEquals(await second, 2); + assertEquals(lazy.initialized, true); + assertEquals(lazy.peek(), { ok: true, value: 2 }); +}); + Deno.test("Lazy.get() resolves falsy values correctly", async (t) => { await t.step("0", async () => { const lazy = new Lazy(() => 0); diff --git a/async/mod.ts b/async/mod.ts index 4cab1dc2b491..de31ea55f712 100644 --- a/async/mod.ts +++ b/async/mod.ts @@ -25,3 +25,4 @@ export * from "./pool.ts"; export * from "./tee.ts"; export * from "./retry.ts"; export * from "./all_keyed.ts"; +export * from "./lazy.ts";