Message Processing

The runtime processes messages one at a time.

You build interactive apps. Events arrive from everywhere: keyboard, mouse, timers, HTTP responses. Coordinating concurrent results feels complex.

Rooibos handles the concurrency. Your update function handles exactly one message per invocation. The runtime schedules everything else.

Recurring Ticks

For animations or polling, re-dispatch the tick in your update function:

def update(msg, model)
  case msg
  when [:tick, _elapsed]
    [model.with(frame: model.frame + 1), Command.tick(0.016, :tick)]
  end
end

Can a tick and a keyboard event arrive at the same time?

No. The runtime serializes all messages. Each frame, it polls for user input, calls update with any event it finds, and dispatches the returned command. Then it drains the background channel, calling update once for each queued message. Two events that occur in the same frame become two sequential calls. Each returns its own command. They never collide.

When to Use Command.batch

Use Command.batch when a single message triggers multiple effects:

when :init
  [model, Command.batch(
    Command.tick(0.016, :tick),
    Command.http(get: "/api/data", :loaded)
  )]

This differs from “two messages arrived simultaneously.” Here, you send two commands at once. Even if both complete in exactly the same time, their results arrive as two distinct calls to update.