Forest Logo
search
package_2

wire

By @elentium

Roblox

Mirrored

Wire

License Wally

A high-performance, signal and request library for Roblox. Wire provides lightweight alternatives to `BindableEvent` and `BindableFunction` with superior performance and a clean, functional API.

Features

  • Blazing Fast — Outperforms GoodSignal, SignalPlus, and FastSignal in benchmarks
  • O(1) Disconnect — Doubly-linked list architecture enables constant-time connection removal
  • Thread Pool Recycling — Efficient coroutine reuse for async operations
  • Parallel Luau Support — First-class support for parallel execution with connectParallel
  • Dual Paradigm — Both event-style signals and RPC-style requests
  • Type-Safe — Full Luau strict mode with exported types
  • Zero Dependencies — Pure Luau implementation
  • Memory-Efficient — Instead of creating a table & using metatables for every signal object, it simply creates an ID and only stores head & tail connections when needed

Installation

Wally

[dependencies]
Wire = "elentium/wire@0.0.4"

Manual

install the roblox-direct/Wire.rbxm and insert in studio

Quick Start

local Wire = require(path.to.Wire)

-- Create a signal
local PlayerDamaged = Wire.signal()

-- Connect a listener
local connection = Wire.connect(PlayerDamaged, function(player, damage)
    print(player.Name .. " took " .. damage .. " damage!")
end)

-- Fire the signal
Wire.fire(PlayerDamaged, player, 25)

-- Disconnect when done
connection:disconnect()

API Reference

Constructors

Wire.signal() -> number

Creates a new signal and returns its entity ID.

local MySignal = Wire.signal()

Wire.request(callback?) -> number

Creates a new request (similar to BindableFunction) and returns its entity ID.

local GetPlayerData = Wire.request(function(player)
    return playerDataStore[player]
end)

Signal Methods

Wire.connect(entityID, callback) -> WireConnection

Connects a callback to a signal. Returns a connection object.

local connection = Wire.connect(MySignal, function(...)
    print("Signal fired with:", ...)
end)

Wire.connectParallel(entityID, callback) -> WireConnection

Connects a callback that runs in parallel (for Parallel Luau).

Wire.connectParallel(HeavyComputation, function(data)
    -- This runs desynchronized from the main thread
    processData(data)
end)

Wire.once(entityID, callback) -> WireConnection

Connects a callback that automatically disconnects after the first fire.

Wire.once(GameStarted, function()
    print("Game has started!")
end)

Wire.onceParallel(entityID, callback) -> WireConnection

Combines once and connectParallel — runs once in parallel, then disconnects.

Wire.fire(entityID, ...) -> ()

Fires a signal synchronously. All callbacks execute sequentially in the current thread.

Wire.fire(MySignal, "arg1", "arg2", 123)

Recommended for non-yielding callbacks. Most performant option.

Wire.fireSafe(entityID, ...) -> ()

Fires a signal synchronously. All callbacks are wrapped in pcall and are executed sequentially in the current thread.

Wire.fire(MySignal, "arg1", "arg2", 123)

Recommended for non-yielding callbacks that can error and you do not want it to stop other connections.

Wire.fireAsync(entityID, ...) -> ()

Fires a signal asynchronously. Each callback runs in its own coroutine.

Wire.fireAsync(MySignal, data)

Use when callbacks may yield (e.g., contain task.wait, HTTP requests, etc.)

Wire.await(entityID) -> ...any

Yields the current thread until the signal fires, then returns the fired arguments.

local damage, attacker = Wire.await(PlayerDamaged)
print("Received damage:", damage, "from", attacker)

Wire.disconnectAll(entityID) -> ()

Disconnects all connections from a signal. Can also serve as a signal destructor.

Wire.disconnectAll(MySignal)

Request Methods

Wire.onInvoke(entityID, callback) -> ()

Sets or updates the callback for a request.

Wire.onInvoke(GetPlayerData, function(player)
    return database:GetAsync(player.UserId)
end)

Wire.invoke(entityID, ...) -> ...any

Invokes a request and returns the result.

local data = Wire.invoke(GetPlayerData, player)

Wire.destroyRequest(entityID) -> ()

Removes the request callback, freeing the reference.

Wire.destroyRequest(GetPlayerData)

Connection Object

connection:disconnect() -> ()

Disconnects the connection from its signal.

local connection = Wire.connect(MySignal, callback)
-- Later...
connection:disconnect()

Performance

Benchmarks run with 10 connections per signal:

OperationWireGoodSignalSignalPlusFastSignal
Fire (100k iterations)0.490s0.552s0.587s0.677s
Disconnect (1k items)0.00007s0.0155s0.00008s0.00009s

Wire achieves the fastest fire times and the fastest disconnect times thanks to its doubly-linked list architecture (O(1) removal vs O(n) for array-based implementations).

Best Practices

Use fire over fireAsync when possible

fire is significantly faster because it avoids coroutine overhead. Only use fireAsync when your callbacks yield.

-- ✅ Good: Non-yielding callback with fire
Wire.connect(DamageDealt, function(amount)
    healthBar:Update(amount)
end)
Wire.fire(DamageDealt, 50)

-- ✅ Good: Yielding callback with fireAsync
Wire.connect(SaveData, function(player)
    dataStore:SetAsync(player.UserId, getData(player))
end)
Wire.fireAsync(SaveData, player)

Handle errors in callbacks when using fire

With fire, an error in any callback will halt execution. Wrap risky code in pcall:

Wire.connect(RiskySignal, function(data)
    local success, err = pcall(function()
        processUnsafeData(data)
    end)
    if not success then
        warn("Handler error:", err)
    end
end)

Clean up connections

Always disconnect connections when they're no longer needed to prevent memory leaks:

local connections = {}

function module:Init()
    table.insert(connections, Wire.connect(Signal1, handler1))
    table.insert(connections, Wire.connect(Signal2, handler2))
end

function module:Destroy()
    for _, conn in connections do
        conn:disconnect()
    end
    table.clear(connections)
end

License

Apache-2.0 — See LICENSE for details.

Links

Package Details

Install command (Click to copy)


Version

0.0.4

License

Apache-2.0

check_circle

Safe for commercial use

infoModified files must carry a notice of changes. If the package ships a NOTICE file, its attributions must be preserved.

Automated license review — not legal advice.