Not sure if it's intended or not but ask(myManageActor,GetValue).pipeTo(sender())
can be implemented as forward
.
class MyActor extends Actor {
lazy val myManageActor: ActorRef = ???
override def receive: Receive = {
case GetFinalValue =>
myManageActor.forward(GetValue)
}
}
forward
is the same as tell
but it preserves the original sender of the messages.
This can be applied to MyActor
and ManagerMyActor
.
In the case of TokenMyActor2
, you should not use
future.map{ result =>
sender ! result
}
as it it breaks akka context encapsulation, as specified in docs
When using future callbacks, such as onComplete, or map such as
thenRun, or thenApply inside actors you need to carefully avoid
closing over the containing actor’s reference, i.e. do not call
methods or access mutable state on the enclosing actor from within the
callback. This would break the actor encapsulation and may introduce
synchronization bugs and race conditions because the callback will be
scheduled concurrently to the enclosing actor. Unfortunately there is
not yet a way to detect these illegal accesses at compile time. See
also: Actors and shared mutable state
You should instead rely on Future(???).pipeTo(sender())
, which is safe to use with sender()
.
After applying these changes, the code does work as expected
case object GetFinalValue
case object GetValue
case object CalculateValue
class MyActor extends Actor {
private val myManageActor: ActorRef =
context.actorOf(Props[ManagerMyActor], "myManageActor")
override def receive: Receive = { case GetFinalValue =>
myManageActor.forward(GetValue)
}
}
class ManagerMyActor extends Actor {
private val myTokenActor =
context.actorOf(Props[TokenMyActor2], "toknMyActor2")
override def receive: Receive = { case GetValue =>
myTokenActor.forward(CalculateValue)
}
}
class TokenMyActor2 extends Actor {
import context.dispatcher
override def receive: Receive = { case CalculateValue =>
val future = Future { "get the string" }
future.pipeTo(sender())
}
}
implicit val timeout = Timeout(3, SECONDS)
implicit val system = ActorSystem("adasd")
import system.dispatcher
val myActor = system.actorOf(Props[MyActor], "myActor")
val future = ask(myActor, GetFinalValue).mapTo[String]
future.foreach { str =>
println(s"got $str")
}
Produces got get the string
.
As a final note, I'd advise not to use ask
pattern within actors. The basic functionality of ask
can be easily achieved with just tell
and forward
. Also the code is shorter and not overloaded with constant need of implicit val timeout