Late reply from async io in Akka
akka, scala
Solution
One way to avoid blocking while waiting for a reply is to send using the `ask` method—a.k.a. `?` operator—which returns a `Future` (unlike `!` which returns `()`).
Using the `onSuccess` or `foreach` methods, you can specify actions to be performed if/when the future is completed with a reply. To use this you need to mix in the `AskSupport` trait:
class ApplicationApi(asyncIo : ActorRef) extends Actor with AskSupport {
def receive = {
case request: GetUser =>
val replyTo = sender
asyncIo ? AsyncGet("http://app/user/" + request.userId) onSuccess {
case response: AsyncResponse =>
val user = parseUser(response.body)
replyTo ! GetUserResponse(user)
}
}
Avoid using this technique to perform any side effect that modifies the state of the `ApplicationApi` actor, because the effect will happen out-of-sync with the receive loop. Forwarding messages to other actors should be safe, though.
By the way, here is a trick to capture the current `sender` as part of the pattern match, avoiding the need to assign it to a variable later.
trait FromSupport { this: Actor =>
case object from {
def unapply(msg: Any) = Some(msg, sender)
}
}
class MyActor extends Actor with FromSupport {
def receive = {
case (request: GetUser) from sender =>
// sender is now a variable (shadowing the method) that is safe to use in a closure
}
}
Problem
I am using akka for some time. I started to see some patterns in my code to solve late reply for async io. Is this implementation ok? There is another way to do a late reply without block? ``` class ApplicationApi(asyncIo : ActorRef) extends Actor { // store senders to late reply val waiting = Map[request, ActorRef]() def receive = { // an actore request for a user, store it to late reply and ask for asyncIo actor to do the real job case request : GetUser => waiting += (sender -> request) asyncIo ! AsyncGet("http://app/user/" + request.userId) // asyncio response, parse and reply case response : AsyncResponse => val user = parseUser(response.body) waiting.remove(response.request) match { case Some(actor) => actor ! GetUserResponse(user) } } } ```