A lightweight, elegant data structure for managing asynchronous data streams in JavaScript/TypeScript. AsyncChain provides a linked-list-based approach to handle producer-consumer patterns with full support for async iteration.
- π Linked List Structure: Efficient memory usage with automatic garbage collection
- β‘ Async/Await Support: Native async iterator implementation
- π― Type Safe: Full TypeScript support with generics
- π Producer-Consumer Pattern: Perfect for event-driven architectures
- π¦ Zero Dependencies: Minimal footprint
- π¨ Flexible: Can be backed by any task object (e.g., EventTarget)
npm install async-chainimport AsyncChain from "async-chain-list";
const chain = new AsyncChain<number>();
// Producer: Push values to the chain
async function producer() {
for (let i = 0; i < 5; i++) {
chain.push(i);
await delay(100);
}
chain.close(); // Signal end of data
}
// Consumer: Iterate through values
async function consumer() {
for await (const value of chain) {
console.log("Received:", value);
}
}
await Promise.all([producer(), consumer()]);AsyncChain can be backed by any task object, making it perfect for event-driven patterns:
import AsyncChain from "async-chain-list";
const eventTarget = new EventTarget();
const chain = new AsyncChain<number, EventTarget>(eventTarget);
// Connect events to the chain
chain.task.addEventListener("data", (e) =>
chain.push((e as CustomEvent).detail),
);
chain.task.addEventListener("close", () => chain.close());
// Producer dispatches events
async function producer() {
for (let i = 0; i < 5; i++) {
chain.task?.dispatchEvent(new CustomEvent("data", { detail: i }));
await delay(100);
}
chain.task?.dispatchEvent(new Event("close"));
}
// Consumer uses async iteration
async function consumer() {
for await (const value of chain) {
console.log("Received:", value);
}
}
await Promise.all([producer(), consumer()]);new AsyncChain<T, P>(task?: P)T: Type of values in the chainP: Type of the backing task object (optional)task: Optional task object that backs the chain (e.g., EventTarget)
Push a new value to the end of the chain. Returns the new back node.
const newBack = chain.push(42);Throws: Error if the chain is already closed.
Close the chain to block further pushes and signal the end to consumers.
chain.close();Get the end node of the chain (useful for pushing values).
const endNode = chain.back;Get the count of values already pushed to the chain from the current node.
const count = chain.current_length;Read-only reference to the backing task object passed during construction.
const eventTarget = chain.task;AsyncChain implements Symbol.asyncIterator for use with for await...of:
for await (const value of chain) {
console.log(value);
}Synchronously iterate through already-pushed values:
for (const value of chain) {
console.log(value); // null for pending values
}Generator that yields all values already pushed to the chain from the current node:
for (const value of chain.current_values()) {
console.log(value); // Only existing values, no waiting
}AsyncChain is implemented as a linked list where:
- Each node contains a value and a reference to the next node
- The
nextreference can be a Promise (pending), another node (data available), or null (end of chain) - Multiple consumers can iterate the same chain independently
- Memory is automatically reclaimed by JavaScript's garbage collector
- Event Streams: Convert event-based APIs to async iterables
- Message Queues: Simple producer-consumer implementations
- Data Pipelines: Stream processing with async/await
- WebSocket/SSE: Handle streaming data from network sources
- Rate-Limited APIs: Queue and process requests asynchronously
Full TypeScript support with generic types:
interface Message {
id: number;
content: string;
}
const chain = new AsyncChain<Message>();
chain.push({ id: 1, content: "Hello" }); // Type-safeMIT Β© Yuxuan Zhang