Events
Connections, channels, tracks and transports fire the events of the specification. Every one of them is an
EventTarget.
Handlers
on() registers a handler, called with the event object. It can be a
plain function or a coroutine function, and on works as a decorator too:
@pc.on('track')
def on_track(event):
print('new track:', event.track.kind)
@pc.on('icecandidate')
async def on_candidate(event):
if event.candidate is not None:
await signaling.send(event.candidate.to_json())
channel.on('message', lambda event: print(event.data))
The names are the ones of the specification, without the on prefix. A name the object doesn’t fire raises
ValueError, which lists the names it does.
Handlers run on the event loop they were registered from, so they have to be registered from a running loop. The native threads never run Python: they post the events to that loop, which delivers them in order. A slow handler delays the events after it, and the media keeps flowing.
One-shot handlers
once() registers a handler that is removed after its first call. To
wait for an event inline, resolve a future from it:
opened = asyncio.get_running_loop().create_future()
channel.once('open', lambda event: opened.set_result(event))
await opened
Removing handlers
pc.remove_listener('track', on_track) # one handler
pc.remove_all_listeners('track') # every handler of an event
pc.off() # every handler of every event
listeners() returns the handlers of an event, and
event_names() the events that have some. Registering the same handler twice has no effect.
What handlers keep alive
An object that can still fire an event you handle stays alive even when nothing references it: a connection with
handlers or a call in flight, a channel with handlers or data to send, a live track with handlers. So end a
connection with close(), not by dropping it; that releases its channels, tracks and
transports too.
When the loop the handlers were registered on closes, they’re removed, its pending calls are cancelled, and what they kept alive is released.
A native object always returns the same Python object, so is works and objects can be dictionary keys. They take
no attributes; keep your state in a dictionary keyed by them.
Errors
An exception in a handler doesn’t stop the other handlers. It goes to the exception handler of the loop, which logs
it by default. Use asyncio.loop.set_exception_handler() to handle them yourself.