Search code examples
scalaakkaakka-cluster

Returning to "sender" from a future inside an actor


I want to do something like this -

class MyActor extends Actor {
....
override def receive = {
  case msg =>
     .... // do something
     Future {
       ... // calculate response
       sender ! response
     }
}
}

// in some other code -
val future = myActorRef ? msg
future.onSuccess {
    ....
}

Would this work? In other words, does Akka's "ask" implementation care if the response was sent back before the "receive" method finishes or not?


Solution

  • Yes it would work, and there is even a built-in akka pattern for it - pipe:

    import akka.pattern.pipe
    
    override def receive = {
      case msg =>
        .... // do something
        Future {
          ... // calculate response
          response
        } pipeTo sender()
    }
    

    There are, however some caveats in your code you should note:

    sender is a function, therefore, when the code inside your Future{...} block executes, the actor may be handling a message from another sender, so you may reply to the wrong sender. To avoid this, evaluate your sender outside the closure:

    val mySender = sender()
    Future {
      ... // calculate response
      mySender ! response
    }
    

    You don't however need to worry about this if you use pipe.


    You are wrapping a future into an actor and calling that actor with ask, which again gives you a future. You should really consider calling the future directly, without actors. If you really need the actor, e.g. because you're isolating some mutable state or message ordering is important, you should be aware that computing the Future will not happen on the actor's thread, so you are losing your state consistency and message ordering - another reason to consider losing the actor, and calling the future directly.