{"id":"synttx/vow","name":"vow","scope":"synttx","platform":"roblox","description":"A Data-Oriented Procedural Promise/Future Library · ×6 faster. ×32 lighter. ×∞ less garbage.","version":"1.0.2","latest":"1.0.2","versions":["1.0.2"],"license":"MPL-2.0","licenseRating":"caution","licenseCaveats":["File-level copyleft: if you modify this package's own source files, those modified files must be made available under MPL-2.0. Using it unmodified in a closed-source game is fine."],"licenseVerified":false,"dependencies":{},"integrity":"28768ef98b7735e557ec650cf0a65a68d892f4ec91f738bf49f9dcc0c319ec06","likes":0,"downloads":0,"install":"forest install synttx/vow","url":"https://forest.dev/p/roblox/synttx/vow","files":"https://api.forest.dev/ai/package/roblox/synttx/vow/files","readme":"# Vow 🤝📜\n\n[![Version](https://img.shields.io/badge/version-1.0-E11D48?style=for-the-badge&logo=github&logoColor=white)](https://github.com/synttx/oss/releases)\n[![License](https://img.shields.io/badge/license-MPL--2.0-3B82F6?style=for-the-badge&logo=mozilla&logoColor=white)](LICENSE)\n[![Luau](https://img.shields.io/badge/language-Luau-8B5CF6?style=for-the-badge&logo=luau&logoColor=white)](https://luau-lang.org)\n[![Dependencies](https://img.shields.io/badge/dependencies-zero-00A2FF?style=for-the-badge&logo=roblox&logoColor=white)](<>)\n\nA data-oriented, procedural Promise/Future library for Roblox Luau.\n\nUnlike conventional asynchronous libraries like [Promise](https://github.com/evaera/roblox-lua-promise) or [Future](https://github.com/RedBlox/Future), **Vow** uses no objects, no metatables, no per-instance tables, and no resolve closures. A Vow is simply a generation-tagged integer handle referencing module-level Structure-of-Arrays (SoA) storage.\n\n---\n\n## Installation\n\n### Via Wally\n\nAdd **Vow** to your `wally.toml` dependencies:\n\n```toml\n[dependencies]\nVow = \"synttx/vow@1.0\"\n```\n\nThen run:\n\n```bash\nwally install\n```\n\n### Manual Installation\n\nDownload and copy the latest release module into your project:\n\n```luau\nlocal Vow = require(path.to.Packages.Vow)\n```\n\nThe module exports a `Vow` type alias for typed Luau:\n\n```luau\nlocal id: Vow.Vow = Vow.new(executor)\n```\n\n---\n\n## Key Features\n\n- 🪶 **Incredibly Lightweight**: Uses integer handles and module-level buffers instead of heavy OOP tables, drastically reducing active memory footprint.\n- 🗑️ **Zero Garbage & GC Pressure**: Generates **0 bytes of heap garbage** during pooled execution, completely eliminating GC frame stutters in hot asynchronous paths.\n- 🚀 **Blazing Fast**: Uses pre-allocated SoA buffers for extreme cache locality and performance.\n- 🛡️ **Bug-Proof & Safe**: Automatically catches stale or recycled handles using generation tags, preventing undefined behavior or silent memory corruption.\n- ♻️ **Pooled & Resilient**: Handles and listener nodes are pooled globally and linked through parallel arrays.\n- 🎯 **Strictly Typed & Zero Bloat**: Built with `--!strict` type safety, zero external dependencies, and no OOP boilerplate.\n\n---\n\n## Benchmarks\n\nVow is architected from the ground up to eliminate Garbage Collector (GC) pressure and minimize CPU cache misses. By abandoning traditional Object-Oriented Programming (OOP) metatables and heap-allocated dictionaries in favor of module-level **Structure-of-Arrays (SoA)** buffers, Vow achieves **zero-allocation pooled resolution**.\n\nAll benchmark scripts are located in [`benchmarks/`](benchmarks/) and can be evaluated against [evaera's Promise](https://github.com/evaera/roblox-lua-promise), [RedBlox's Future](https://github.com/RedBlox/Future), and [YetAnotherClown's Future](https://github.com/YetAnotherClown/Future) using Roblox Studio.\n\n---\n\n### Memory Allocation & GC Pressure\n\nWhile execution speed benchmarks show Vow performing at or above OOP leaders, **memory efficiency and GC pressure** are where Vow completely outshines all other libraries. Traditional Promise implementations allocate Luau tables, metatables, and wrapper closures per instance, which can generate huge amounts of heap garbage across millions of asynchronous cycles.\n\n```\n+-----------------------------------------------------------------------------------+\n|               STEADY-STATE GARBAGE GENERATION (200,000 CYCLES)                    |\n+-----------------------------------------------------------------------------------+\n|  evaera Promise   ███████████████████████████████████ 1.74 GB   (8940.03 B/cycle) |\n|  YetAnotherClown  ███████████████                       520 MB  (2666.96 B/cycle) |\n|  RedBlox Future   ████                                  140 MB    (720.00 B/cycle)|\n|  Vow              ▏                                       0 KB      (0.00 B/cycle)|\n+-----------------------------------------------------------------------------------+\n```\n\n#### Peak Active RAM & GC Footprint Comparison\n\n| Library             | Cold Active RAM (50k Items) | Warm Active RAM (50k Items) | Bytes per Chain | Total GC Garbage (200k Cycles) | Bytes per Cycle |\n| :------------------ | :-------------------------- | :-------------------------- | :-------------- | :----------------------------- | :-------------- |\n| **Vow (SoA)**       | **15,235.00 KB**            | **0.00 KB**                 | **312.01 B**    | **0.00 KB**                    | **0.00 B**      |\n| **RedBlox Future**  | 35,157.00 KB                | 35,156.00 KB                | 720.02 B        | 140,625.00 KB _(~140 MB)_      | 720.00 B        |\n| **YetAnotherClown** | 132,812.00 KB               | 132,812.00 KB               | 2719.99 B       | 520,891.00 KB _(~520 MB)_      | 2666.96 B       |\n| **evaera Promise**  | 370,978.00 KB               | 26,128.00 KB                | 7597.63 B       | 1,746,099.00 KB _(~1.74 GB)_   | 8940.03 B       |\n\n#### Why Vow Dominates Memory & GC\n\n1. **Minimal Per-Instance Allocations**: Traditional Promise implementations allocate Luau tables, metatables, and wrapper closures per instance, costing hundreds or thousands of bytes. Vow scopes are opaque integers occupying minimal space in module-level SoA arrays.\n2. **0.00 KB Steady-State Garbage**: In high-frequency async loops, OOP libraries continuously allocate and discard tables, flooding the Luau Garbage Collector. Vow recycles handle IDs and uses pre-allocated buffers, producing **0 bytes of heap garbage**.\n3. **No Frame Stutters**: By eliminating heap churn, Vow ensures that high-frequency async execution never triggers GC sweep pauses during gameplay.\n\n---\n\n### Collapsible Benchmark Suite\n\nClick any benchmark below to view the script, visual benchmark results, architectural analysis, and developer takeaways.\n\n<details>\n<summary><b>1. Instantiation & Immediate Resolution</b> (<code>01_Instantiation.bench.luau</code>)</summary>\n\n<br>\n\n![](./assets/Bench01.png)\n\n- **Script**: [`benchmarks/01_Instantiation.bench.luau`](benchmarks/01_Instantiation.bench.luau)\n- **Description**: Measures the overhead of creating and resolving 1,000 asynchronous values.\n- **Results (50% Median)**:\n    - **RedBlox Future**: `1365.00 µs`\n    - **Vow**: `1652.00 µs`\n    - **YetAnotherClown**: `3102.00 µs`\n    - **evaera Promise**: `6747.00 µs`\n- **Why Vow Stands Here**:\n    - RedBlox is slightly faster at sheer bare instantiation because Vow performs some internal buffer pool acquisitions and generation checks.\n    - However, Vow is 4x faster than evaera's Promise and produces absolutely no heap garbage in the long run.\n- **What It Means to the Developer**: Rapid creation and resolution of promises is incredibly fast, and with Vow, you don't pay the hidden GC tax later.\n\n</details>\n\n<details>\n<summary><b>2. Chaining & Transformation</b> (<code>02_Chaining.bench.luau</code>)</summary>\n\n<br>\n\n![](./assets/Bench02.png)\n\n- **Script**: [`benchmarks/02_Chaining.bench.luau`](benchmarks/02_Chaining.bench.luau)\n- **Description**: Measures the overhead of attaching fulfillment callbacks (`andThen` / `After`) to a resolved value to transform it 1,000 times.\n- **Results (50% Median)**:\n    - **RedBlox Future**: `2382.00 µs`\n    - **Vow**: `3550.00 µs`\n    - **YetAnotherClown**: `3695.00 µs`\n    - **evaera Promise**: `20321.00 µs`\n- **Why Vow Stands Here**:\n    - Vow is highly competitive in execution speed, sitting neck-and-neck with other lightweight Futures, while outperforming robust implementations like evaera Promise by 5x.\n    - Chaining in Vow involves appending listener nodes to parallel SoA arrays rather than closures wrapped in closures.\n- **What It Means to the Developer**: Deeply chained asynchronous pipelines process rapidly and efficiently.\n\n</details>\n\n<details>\n<summary><b>3. Aggregation (All / JoinAll)</b> (<code>03_Aggregation.bench.luau</code>)</summary>\n\n<br>\n\n![](./assets/Bench03.png)\n\n- **Script**: [`benchmarks/03_Aggregation.bench.luau`](benchmarks/03_Aggregation.bench.luau)\n- **Description**: Measures the overhead of grouping 100 pending asynchronous tasks into a single aggregate value that resolves when all inputs resolve.\n- **Results (50% Median)**:\n    - **Vow**: `230.00 µs`\n    - **YetAnotherClown**: `1197.00 µs`\n    - **evaera Promise**: `1497.00 µs`\n- **Why Vow Stands Here**:\n    - Vow (`230.00 µs`) is undeniably faster at aggregation—over 5x faster than its competitors.\n    - Vow pre-validates inputs and manages aggregate completion with simple integer counters rather than massive internal table loops and `ipairs`.\n- **What It Means to the Developer**: Grouping massive lists of requests (e.g. `Vow.all`) is effectively instantaneous.\n\n</details>\n\n<details>\n<summary><b>4. Memory Allocation & GC Pressure</b> (<code>MemoryBenchmark.luau</code>)</summary>\n\n<br>\n\n![](./assets/MemBench.png)\n\n- **Script**: [`benchmarks/MemoryBenchmark.luau`](benchmarks/MemoryBenchmark.luau)\n- **Description**: Measures Peak Active RAM footprint (Cold and Warm starts across 50,000 items) and Total GC Garbage generated across 200,000 cycles.\n- **Results**:\n    - **Peak Active RAM (50k Items)**:\n        - **Vow**: **`15,235.00 KB` (`312.01 Bytes per chain`)**\n        - **RedBlox Future**: `35,157.00 KB` (`720.02 Bytes per chain`)\n        - **YetAnotherClown**: `132,812.00 KB` (`2719.99 Bytes per chain`)\n        - **evaera Promise**: `370,978.00 KB` (`7597.63 Bytes per chain`)\n    - **Total GC Garbage Generated (200,000 Cycles)**:\n        - **Vow**: **`0.00 KB` (`0.00 Bytes per cycle`)**\n        - **RedBlox Future**: `140,625.00 KB` (`720.00 Bytes per cycle`)\n        - **YetAnotherClown**: `520,891.00 KB` (`2666.96 Bytes per cycle`)\n        - **evaera Promise**: `1,746,099.00 KB` (`8940.03 Bytes per cycle`)\n- **Why Vow Demolishes All Other Libraries**:\n    - Traditional OOP libraries allocate Luau tables, metatables, and closures per scope, generating hundreds of megabytes of GC garbage.\n    - Vow uses zero per-instance allocations. Vows are integer handles into module-level SoA arrays, and recycled handles reuse warm buffers without touching the heap.\n- **What It Means to the Developer**: Vow eliminates Garbage Collector pauses. Your game runs smoothly with zero GC spikes even under intense, continuous asynchronous churn.\n\n</details>\n\n---\n\n## Usage\n\n```luau\nlocal Vow = require(path.to.Vow)\n\n-- Create a new Vow\nlocal id = Vow.new(function(id)\n\ttask.defer(function()\n\t\tVow.resolve(id, \"ready\")\n\tend)\nend)\n\n-- Chain with andThen\nlocal nextId = Vow.andThen(id, function(value)\n\tprint(value) -- prints: ready\n\treturn \"finished\"\nend)\n\n-- Await the result\nlocal ok, value = Vow.await(nextId)\nprint(value) -- prints: finished\n\n-- IMPORTANT: You must manually destroy Vows to free up slots in the pool\nVow.destroy(id)\nVow.destroy(nextId)\n```\n\n---\n\n## API Reference\n\n| Function  | Signature                                          | Description                                                              |\n| :-------- | :------------------------------------------------- | :----------------------------------------------------------------------- |\n| `new`     | `(executor: (id: Vow) -> ()) → Vow`                | Acquire a new Vow and execute the provided function.                     |\n| `resolve` | `(id: Vow, value: any) → ()`                       | Resolves the Vow with a single value. Can adopt another live Vow.        |\n| `reject`  | `(id: Vow, value: any) → ()`                       | Rejects the Vow with the given value.                                    |\n| `cancel`  | `(id: Vow) → ()`                                   | Cancels the Vow and flows downwards to its `andThen`/adoption children.  |\n| `destroy` | `(id: Vow) → ()`                                   | Disposes the Vow, detaches it, and recycles the handle back to the pool. |\n| `andThen` | `(id: Vow, handler: ((value: any) -> any)?) → Vow` | Chains a callback to run when the Vow resolves.                          |\n| `await`   | `(id: Vow) → (boolean, any)`                       | Yields the current thread until the Vow settles.                         |\n| `all`     | `(vows: {Vow}) → Vow`                              | Returns an aggregate Vow that resolves when all input Vows resolve.      |\n| `race`    | `(vows: {Vow}) → Vow`                              | Returns an aggregate Vow that settles as soon as any input Vow settles.  |\n\n---\n\n## Internal Architecture\n\nVow splits its data across parallel module-level Structure-of-Arrays (SoA) buffers:\n\n```luau\nvowValues           : { any }      -- Payloads for each Vow\nvowAggregateResults : { { any }? } -- Payloads for Vow.all aggregates\nvowGens             : buffer       -- Generation tags\nvowStates           : buffer       -- Pending / Resolved / Rejected / Cancelled\nvowHeads            : buffer       -- Listener list heads\nvowTails            : buffer       -- Listener list tails\nvowOwnedHeads       : buffer       -- Owned listener list heads\nvowOwnedTails       : buffer       -- Owned listener list tails\nvowFree             : buffer       -- u32 recycled handle stack\n```\n\nA parallel set of buffers handles the listener node SoA (`nodeKinds`, `nodeNext`, `nodePrevious`, etc.).\n\n---\n\n## Important Notes\n\n> [!NOTE]\n> **Adoption Mechanics**: `Vow.resolve` adopts another live Vow passed as its value. A value returned from an `andThen` fulfillment callback is also adopted. Adoption cycles (`A -> B -> A`) are detected and rejected to prevent infinite hangs.\n\n> [!IMPORTANT]\n> **Manual Memory Management**: Because Vows are opaque integer handles, Luau's garbage collector cannot automatically discover when a settled Vow is no longer needed. **Developers MUST call `Vow.destroy(id)`** when completely finished with a Vow to release it back into the pool.\n\n---\n\n## Metadata\n\n- **Version**: `1.0`\n- **Author**: `checcerr` | `fridayqx`\n- **License**: [Mozilla Public License 2.0 (MPL-2.0)](LICENSE)\n","readmeTruncated":false}