Lox Nexus

Abstract Class: Disposable

Base class for everything that has a lifetime: it owns timers, listeners and child objects and releases them all in one dispose(). Extend it for a helper your plugin creates (a poller, an API client with a connection); Plugin, Sensor, Actuator and Action already extend it.

Resources registered through own(), every(), after() or listen() are released in reverse registration order when dispose() is called, so a subclass never has to remember to clean up after itself. The registration helpers are protected on purpose: only the owner decides what lives and dies with it.

Registering anything after dispose() throws, so a late callback cannot leak a timer into a dead object.

Plugins, sensors and actuators are Disposables; the runtime calls their dispose() when a signal is removed or edited and when the instance stops, so plugin code rarely calls it itself. A class you own() should extend Disposable too (or implement DisposableLike) and register its own timers and listeners the same way:

class Poller extends Disposable {
  start() { this.every(5000, () => void this.poll()) }
}
// in the plugin: readonly poller = this.own(new Poller())   (call poller.start() from start())

A callback given to every(), after() or listen() that throws is an uncaught exception, which ends the worker (the host restarts it); catch errors inside it, and report them with the plugin's log or status. A callback that returns a rejected promise goes to Plugin.unhandledRejection().

Extended by

Implements

Constructors

Constructor

new Disposable(): Disposable

Returns

Disposable

Properties

MAX_TIMER_MS

readonly static MAX_TIMER_MS: 2147483647 = 2_147_483_647

the largest delay setTimeout/setInterval honour; Node turns anything longer into 1 ms

Accessors

isDisposed

Get Signature

get isDisposed(): boolean

true from the moment dispose() starts, not when it finishes

Returns

boolean

Methods

dispose()

dispose(): Promise<void>

Releases everything registered with this object. Plugin code seldom needs it: the runtime disposes plugins, sensors and actuators, and own() disposes children. Call it yourself for an object you created without own(), or to end a child early.

Runs all teardowns in reverse registration order and keeps going when one fails. Idempotent: every call returns the same promise, so a second caller also waits for async teardowns.

Returns

Promise<void>

Throws

AggregateError collecting every teardown error, after all teardowns have run

Implementation of

DisposableLike.dispose


onDispose()

protected onDispose(teardown): void

Registers a callback that runs on dispose(), for cleanup the helpers don't cover (closing a socket, flushing a buffer). Async callbacks are awaited one after the other, last registered first, so a flush can use what was registered before it. Keep the total under the five seconds a stop has.

Parameters

teardown

Teardown

Returns

void

Throws

Error if already disposed


useClock()

protected useClock(clock): void

Sets the clock for every() and after() calls from now on, unless one is set already; without one they run on the system clock. Timers started before keep the clock they started on. Plugin, Sensor and Actuator set it from the plugin's context.

Parameters

clock

Clock | undefined

Returns

void


currentClock()

protected currentClock(): Clock

The clock this object runs on: every() and after() use it, and a Disposable that isn't a plugin, sensor or actuator reads the time from it (this.currentClock().now()).

Returns

Clock


own()

protected own<T>(resource): T

ties the lifetime of a child resource to this object; returns it so construction and registration fit in one expression. A Disposable child without a clock of its own runs on this one's, looked up when a timer starts, so a chain of owners (plugin → client → poller) shares the plugin's clock. Timers a child starts in its constructor, before it is owned, run on the system clock.

Type Parameters

T

T extends DisposableLike

Parameters

resource

T

Returns

T


every()

protected every(ms, fn): () => void

runs fn every ms milliseconds until disposed or until the returned cancel function is called. The first run is after ms, not at once; call fn yourself first if you want an immediate run. Runs are not queued: a slow async fn can overlap its next run, so guard it yourself if needed.

Parameters

ms

number

fn

() => void

Returns

() => void

Throws

RangeError unless 0 < ms <= MAX_TIMER_MS


after()

protected after(ms, fn): () => void

runs fn once after ms milliseconds unless disposed before; returns a cancel function. Both firing and cancelling forget the teardown, so repeated use does not accumulate entries. Delays beyond MAX_TIMER_MS are scheduled in steps, so they don't fire early.

The delay counts elapsed time, not wall-clock time: a clock step (an NTP sync after boot) is not noticed. For a deadline given as a wall-clock time, wait in shorter slices and re-check the time each time.

Parameters

ms

number

fn

() => void

Returns

() => void

Throws

RangeError for negative or non-finite delays


listen()

protected listen(emitter, event, listener): void

Subscribes listener to event of an EventEmitter (or anything with on/off) and removes it again on dispose(), so a long-lived emitter such as the plugin's poller does not keep a disposed sensor alive. Prefer it to calling emitter.on yourself.

Parameters

emitter

Listenable

event

string | symbol

listener

Listener

Returns

void