Synchronous Dispatching

The EventDispatcher class provides synchronous, blocking event dispatching. Use this when your listeners perform regular (non-async) operations.

When to Use EventDispatcher

Choose EventDispatcher when:

  • Your listeners perform synchronous operations (database queries, file I/O, calculations)

  • You’re working with existing synchronous code

  • You don’t need async/await functionality

  • You want simpler, more straightforward code flow

Creating a Dispatcher

Create an instance of EventDispatcher:

from whistle import EventDispatcher

dispatcher = EventDispatcher()

All listeners registered with this dispatcher must be regular (synchronous) functions. Attempting to register an async function will raise a TypeError.

Multiple Listeners

Multiple listeners can respond to the same event. They execute in the order they were registered (when no priority is specified):

from whistle import EventDispatcher


def main():
    """Demonstrate multiple listeners for a single event."""
    dispatcher = EventDispatcher()

    # Define multiple listener functions
    def turn_off_lights(event):
        print("1. Turning off the lights")

    def start_music(event):
        print("2. Starting the music")

    def open_curtains(event):
        print("3. Opening the curtains")

    # Register all listeners for the same event
    dispatcher.add_listener("show.start", turn_off_lights)
    dispatcher.add_listener("show.start", start_music)
    dispatcher.add_listener("show.start", open_curtains)

    # Dispatch the event - all listeners will be called
    dispatcher.dispatch("show.start")


if __name__ == "__main__":
    main()

Running this example produces:

1. Turning off the lights
2. Starting the music
3. Opening the curtains

All three listeners execute sequentially when the event is dispatched.

Using the @listen Decorator

For cleaner code, use the @listen decorator to register listeners:

from whistle import EventDispatcher


def main():
    """Demonstrate listener registration using decorators."""
    dispatcher = EventDispatcher()

    # Use decorator to register listeners
    @dispatcher.listen("user.login")
    def on_user_login(event):
        print(f"User logged in - event: {event.name}")

    @dispatcher.listen("user.logout")
    def on_user_logout(event):
        print(f"User logged out - event: {event.name}")

    # Dispatch events
    dispatcher.dispatch("user.login")
    dispatcher.dispatch("user.logout")


if __name__ == "__main__":
    main()

The decorator provides a more elegant syntax and makes the event-listener relationship clear at the point of definition.

Dispatching Events

The dispatch() method triggers an event:

event = dispatcher.dispatch("event.name")

Key behaviors:

  • If no Event object is provided, the dispatcher creates one automatically

  • The event object is passed to all registered listeners

  • Listeners execute sequentially in priority order

  • The event object is returned after all listeners execute

Providing Custom Events

You can provide your own event object:

from whistle import Event

custom_event = Event()
custom_event.my_data = "some value"

dispatcher.dispatch("event.name", custom_event)

Listeners can then access event.my_data. See Custom Event Classes for more details on creating custom event classes.

API Summary

EventDispatcher provides:

  • add_listener(event_id, listener, priority=0): Register a listener

  • remove_listener(event_id, listener): Unregister a listener

  • listen(event_id, priority=0): Decorator for registering listeners

  • dispatch(event_id, event=None): Trigger an event synchronously

  • get_listeners(event_id=None): Retrieve registered listeners

  • has_listeners(event_id=None): Check if listeners are registered

See also Asynchronous Dispatching for the async equivalent.