Ask Pattern
The ask pattern provides request–response semantics between actors. An actor sends a message and waits for a reply, typically by using a future or awaiting a Task
.
Basic Flow
In Proto.Actor you can use Context.Request
when the sender expects the recipient to know who sent the message. For an awaitable response, Context.RequestAsync<T>
or PID.RequestAsync<T>
returns a Task<T>
that completes when the reply arrives.
// ask a target actor for a reply
var response = await pid.RequestAsync<MyReply>(new MyRequest());
future := system.Root.RequestFuture(pid, &MyRequest{}, time.Second)
response, err := future.Result()
if err != nil {
logger.Error("request failed", "err", err)
return
}
reply := response.(*MyReply)
When to Use
- Querying another actor for state or service results.
- Bridging between actor code and external await-based APIs.
- Providing back-pressure by limiting concurrent pending requests.
Tell, Don't Ask
In an asynchronous system it's often better to emit events about your state rather than having others ask for it. By telling peers about changes, actors stay loosely coupled and can react when they are ready.
public class Counter : IActor
{
private int _value;
public Task ReceiveAsync(IContext context)
{
switch (context.Message)
{
case Increment _:
_value++;
// tell others instead of expecting them to ask
EventStream.Instance.Publish(new CountChanged(_value));
break;
}
return Task.CompletedTask;
}
}
type Counter struct {
value int
}
func (c *Counter) Receive(context actor.Context) {
switch context.Message().(type) {
case *Increment:
c.value++
context.ActorSystem().EventStream.Publish(&CountChanged{Value: c.value})
}
}
Listeners subscribe to the emitted events and can maintain local state without performing additional request–response roundtrips.
Reentrancy and Ask
Waiting on RequestAsync
inside an actor's receive method suspends message processing until the task completes. Combine the ask pattern with Reentrancy (RequestReenter
or ReenterAfter
) to keep the actor responsive. Without reentrancy, the actor cannot handle other messages and may cause a deadlock.
Deadlock Example
If Actor A awaits a reply from Actor B while B simultaneously awaits a reply from A, neither actor can proceed. Reentrancy or redesigning the communication flow breaks the cycle.
See Actor Communication for an overview of messaging APIs and PID for additional request options.