Advanced: Client Results & Cancellation
Client results
The server can invoke a method on a single client and wait for its return
value. The client must have registered a handler for the method; the handler's
non-None return value is sent back as the result.
class EchoHub(Hub):
async def ping_client(self) -> str:
return await self.clients.caller.invoke("client_method", "hello-from-server")
Client side:
async def client_method(text: str) -> str:
return f"ack:{text}"
connection.on("client_method", client_method)
result = await connection.invoke("ping_client")
print(result) # "ack:hello-from-server"
invoke(method, *args)returns the client result, or raisesHubExceptionif the client returns an error.- An optional
timeoutraisesSignalRErrorif the client does not answer in time. - Client results are tracked per connection with server-generated invocation
ids (
s1,s2, …).
How it works
- The server registers a future for a fresh invocation id.
- It sends an
Invocationmessage to the client with that invocation id. - The client runs its handlers and replies with a
Completioncarrying the return value (or an error). - The server resolves the future.
Cancellation
Clients can cancel an in-flight invocation with CancelInvocation. The server
responds in two ways:
- Sets the invocation's
cancellation_token(anasyncio.Event). - Cancels the underlying task running the hub method.
class ProbeHub(Hub):
async def cancelable(self, n: int, cancellation_token: Any = None) -> int:
try:
for i in range(n):
await asyncio.sleep(0.05)
if cancellation_token is not None and cancellation_token.is_set():
return i
return n
except asyncio.CancelledError:
return -1
The aiosignalr client sends a CancelInvocation automatically when a
stream() iterator is abandoned early.
Parallel invocations
By default each connection processes one invocation at a time
(maximum_parallel_invocations = 1), mirroring the reference server. Raise it
to allow concurrent hub methods on a single connection:
server = SignalRServer(MyHub, options=ServerOptions(maximum_parallel_invocations=4))
Streaming methods always start on their own task so they do not block the invocation semaphore while streaming.