LazyPromise

LazyPromise

A lazy, cancelable async primitive for TypeScript with typed errors and dependency injection

Open Source

Gallery

About LazyPromise

LazyPromise is an open-source TypeScript library for asynchronous work that should start only when something subscribes and should stop cleanly when it is no longer needed. It sits between a native Promise and an RxJS Observable. Like a promise, it settles once and offers a familiar set of mapping, catching, finalization, combination, and racing operations. Like an observable, it is lazy, can run again for each subscription, and returns a disposable subscription that tears down the work. The project presents this smaller model as an alternative for developers who want several ideas from Effect without adopting a broad effect system.

A LazyPromise is created with a callback that receives a sink for resolving or rejecting. That callback can return teardown logic, which the library runs at most once. Nothing happens until code subscribes, and disposing a subscription cancels the upstream LazyPromises from which it was derived. Each new subscription runs the constructor callback again, so the primitive doesn't cache or share a previous result by default. The implementation enforces single settlement, prevents values after disposal, runs teardown before delivery, and flattens a LazyPromise returned as a value. Notifications are synchronous unless the caller explicitly places work in a microtask. That behavior removes the automatic microtask hop of native promises, but it also means developers need to understand ordering when they mix LazyPromise with ordinary async code.

The everyday API deliberately mirrors native Promise concepts while using names that avoid JavaScript's special treatment of thenables. The main transformation method is map rather than then, alongside catch, finally, all, allKeyed, race, any, box, rejecting, and defer. A mapping callback that returns another LazyPromise is flattened automatically, so the library doesn't need separate map and flatMap methods. The pipe method accepts a function or a custom operator for teams that prefer a compositional style. Conversion helpers support interoperation with eager native promises, so an application doesn't have to replace every async boundary at once. The package runs in modern browsers and supports Node.js 20.16 or later within the 20 series and Node.js 22.3 or later, with a browser playground for trying the model before installation.

For longer workflows, fromGen provides generator syntax that plays the role of async and await. A generator yields to a LazyPromise with yield*, then continues with normal JavaScript control flow when the value arrives. Cancellation propagates through chained operators and generator-based work. A cancellation while a generator is waiting doesn't run a finally block, which is an important distinction for cleanup code. The project also supports type-safe errors by wrapping expected failures in ErrorBox values. The catchBoxed operator handles them, while subscribe and conversion APIs produce type errors when boxed failures haven't been handled or explicitly allowed. Unexpected failures can still travel through the rejection channel, keeping routine domain outcomes distinct from bugs or exceptional conditions.

Dependency injection is carried by the LazyPromise type itself. A lazy callback can receive a dependency when it runs, and combinators merge the requirements of their inputs, so combining work that needs two services produces a value that requires both. The inject method can satisfy a dependency earlier, and the documentation suggests symbol-keyed objects when several modules share services. This is particularly useful in tests, where the same async workflow can receive production implementations or mocks without reaching into global state. It is a compact mechanism, but the extra type parameter and intersection types will feel most natural to teams already comfortable with advanced TypeScript.

LazyPromise fits browser interfaces, cancellable requests, event-driven application code, and libraries that need lazy async composition without bringing in a larger runtime. It is not a drop-in native Promise replacement. Its map naming, synchronous delivery, repeated execution per subscription, generator conventions, and explicit ErrorBox model all require deliberate adoption. Shared caching is intentionally left to userland state, and the library omits direct equivalents for some native helpers, including allSettled and withResolvers. The package is free under the MIT license, installs as @lazy-promise/core, and has no paid plan. Its focused documentation and StackBlitz playground make evaluation straightforward, while its young community and specialized semantics suggest introducing it first in a contained workflow.

Key Features

  • Lazy cancelable async execution
  • Promise-like composition operators
  • Generator-based control flow
  • Type-safe boxed errors
  • Typed dependency injection
  • Native Promise interoperation

Pros & Cons

What we like

  • Starts work only when a subscriber needs it
  • Cancellation propagates through derived operations
  • Keeps the API smaller than a full effect system
  • MIT license allows broad commercial use

Room for improvement

  • Synchronous delivery needs careful ordering
  • Not a drop-in replacement for native Promise
  • Shared caching is left to userland
  • Young library with a smaller community

Frequently Asked Questions

What is LazyPromise?
LazyPromise is a single-settlement async primitive for TypeScript that starts on subscription and supports cancellation. It combines a Promise-like API with selected Observable and Effect ideas.
How is LazyPromise different from a native Promise?
A native Promise starts eagerly and can't be canceled by itself. LazyPromise runs separately for each subscription, exposes a disposable subscription, and can propagate cancellation upstream.
Does LazyPromise support typed errors?
Yes. Expected failures can be wrapped in ErrorBox and handled with catchBoxed, and TypeScript flags unresolved boxed errors at subscription boundaries. Unexpected errors can still use the rejection channel.
Is LazyPromise free?
Yes. The @lazy-promise/core package is open source under the MIT license, and the project doesn't advertise a paid plan.

Best For

Canceling requests when views unmountComposing lazy TypeScript workflowsTesting async code with injected dependenciesRepresenting expected failures in types

Featured in

Alternatives to LazyPromise

Reviews (0)

No reviews yet

Be the first to share your experience with LazyPromise

Sign in to write a review

Badge builder

Add LazyPromise to your website

Choose a badge style and size, preview it here, then copy the generated HTML. Badge images are self-contained SVGs and do not require an external script.

LazyPromise badge preview
<a href="https://toolindex.net/tools/lazypromise?ref=badge" target="_blank" rel="noopener">
  <img src="https://toolindex.net/badge/lazypromise/medium.svg" alt="LazyPromise - Listed on Tool Index" width="180" height="50" />
</a>

How to use the badge

  1. 1. Pick the style, size, and theme that fit your layout.
  2. 2. Copy the generated HTML from the code block.
  3. 3. Paste it into your footer, homepage, or press page.

Standard badge available

The standard listing badge is available now. Score and circle badges are limited to tools currently ranked in the top 10 of a category.

Badge clicks return visitors to this profile with a referral tag so the source remains identifiable.