When our fetcher manager receives a GiveMeWork
request, we will need to send work back to the correct fetcher. We can access the actor who sent a message using the sender
method, which is a method of Actor
that returns the ActorRef
corresponding to the actor who sent the message currently being processed. The case
statement corresponding to GiveMeWork
in the fetcher manager is therefore:
def receive = {
case GiveMeWork =>
login = // get next login to fetch
sender ! Fetcher.Fetch(login)
...
}
As sender
is a method, its return value will change for every new incoming message. It should therefore only be used synchronously with the receive
method. In particular, using it in a future is dangerous:
def receive = {
case DoSomeWork =>
val work = Future { Thread.sleep(20000) ; 5 }
work.onComplete { result =>
sender ! Complete(result) // NO!
}
}
The problem is that when the future is completed 20 seconds after the message is processed, the actor will, in all likelihood, be processing a different message so the return value of sender
will have changed. We will thus send the Complete
message to a completely different actor.
If you need to reply to a message outside of the receive
method, such as when a future completes, you should bind the value of the current sender to a variable:
def receive = {
case DoSomeWork =>
// bind the current value of sender to a val
val requestor = sender
val work = Future { Thread.sleep(20000) ; 5 }
work.onComplete { result => requestor ! Complete(result) }
}