Skip to content

on-the-ground/daemonizer-js

Repository files navigation

🌀 Daemonizer

A minimal async control flow framework for writing browser and Node.js daemons.

Daemonizer helps you manage long-running async event loops, safely respond to cancellation, and yield control to the macro task queue—without starving the event loop.


🚀 Installation

yarn add daemonizer
# or
npm install daemonizer

✨ Features

  • Abort-aware event loop
  • Task group with lifecycle tracking
  • Yielding mechanism to avoid blocking
  • Bounded queue for backpressure
  • Works in both Node.js and browser

🧪 Try in Your Browser

Run the daemon in your browser with no setup:
👉 examples/example.html

Open your browser console and watch tick: messages stream in real time!

<!-- examples/example.html -->
<!DOCTYPE html>
<html>
  <body>
    <script type="module">
      import { Daemon } from "https://esm.sh/@on-the-ground/daemonizer@latest";

      ///////////// Daemon Example /////////////

      let controller = new AbortController();
      const daemon = new Daemon(controller.signal, async (_signal, event) => {
        await new Promise((r) => setTimeout(r, 1000));
        console.log("tick:", event);
      });

      for (let i = 1; i <= 5; i++) {
        await daemon.pushEvent(i);
      }

      setTimeout(() => controller.abort(), 3000);

      console.log("waiting the daemon down");
      await daemon.close();
      console.log("the daemon got down");

      // Results:
      // waiting the daemon down
      // tick: 1
      // tick: 2
      // the daemon got down
      // tick: 3 <- long running task, use strictInterval to abort it
    </script>
  </body>
</html>

🧠 API Overview

Daemon – The Core Abstraction

import { Daemon } from "@on-the-ground/daemonizer";

const daemon = new Daemon(signal, async (msg) => {
  // Your background task handler
  console.log("received:", msg);
});

// Push a task into the daemon's queue
await daemon.pushEvent({ type: "log", content: "hello" });

// Gracefully shut down when you're done
await daemon.close();

✅ Features

  • Runs background tasks with structured concurrency
  • Backpressure-safe via internal bounded queue
  • Auto-shuts down when AbortSignal is aborted
  • One-liner setup: no boilerplate, no ceremony

🧰 Low-level Tools (also exported)

launchEventLoop(signal, taskGroup, events, handler)

Runs a long-lived, abortable event loop over an AsyncIterable.
Automatically yields to the macro task queue to prevent starvation.

TaskGroup

Tracks the lifecycle of multiple concurrent async tasks.

  • add(n = 1)
  • done()
  • wait(): Promise<void>

MacroTaskYielder

Yields only if enough time has passed since the last yield.

BoundedQueue<T>

A fixed-capacity async queue. Backpressure-aware and safe for multiple consumers.


🌐 Compatibility

Daemonizer is fully compatible with:

  • ✅ Node.js (v16+)
  • ✅ Modern browsers (via bundlers like Vite, Webpack, etc.)

No external runtime dependencies.


📜 License

MIT © 2025 Joohyung Park
github.com/on-the-ground/daemonizer


"The name was subconsciously inspired by countless replays of Judas Priest’s 'Demonizer'."

About

Turn async loops into resilient daemons—in Node or the browser.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published