score:9

Accepted answer

As written in the Akka's Future documentation, using Await blocks current thread until awaiting for the result.

Example

import scala.concurrent.Await
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._

implicit val timeout = Timeout(5 seconds)
val future = actor ? msg // enabled by the “ask” import
val result = Await.result(future, timeout.duration).asInstanceOf[String]

This will cause the current thread to block and wait for the Actor to 'complete' the Future with it's reply.

Use With Actors

score:0

If you don't want to block on the calling side then don't use Await, use the non blocking callbacks instead like onSuccess, onFailure and onComplete. When you do this, a future task is put into whatever ExecutionContext is in scope at time of ask (?). When a response is received, this callback is invoked asynchronously via the ExecutionContext. This way you avoid blocking all together in the thread that is making the request to the actor and then the callback is handled in the thread pool tied to the ExecutionContext.

In addition, I believe the inbox stuff you mention is geared towards testing out actor stuff in the REPL (at least that's what the docs on ActorDsl) states. Stick with the approach you have of using ask from outside the actor. Let akka create the short lived actor that it needs for the communication under the hood for non-actor to actor calls. Then just switch to a non blocking callback as I suggested above. I believe that's what you are looking for.

score:5

The Await.receive is part of the Scala concurrency API and has nothing to do with actors. Its purpose is the block the current thread until the provided future completes, or the timeout limit kicks in and the whole thing ends in a timeout exception.

The ask operator ? will indeed create a temporary actor with the sole purpose of awaiting for the reply from the actor pointed to by the aref variable and completing the future you got when you called the ask operator with the received reply.

So your code is essentially blocking the entire thread. As it was indicated, if you want to free up the current thread and continue doing some other work you can attach a callback to the future.

implicit val ctx: ExecutionContext = //provide execution context here
implicit val timeout: Timeout = // provide timeout here
aref ? GroupReceive(fromRank)) onSuccess { res =>
   //do something with res here, asynchronously
}
// some other code which runs without being blocked...

The above code can be rewritten with the actor DSL you mentioned above:

import akka.actor.ActorDSL._
implicit val actorSystem: ActorSystem = // provide an actor system here or any actor ref factory

actor(new Act {
  aref ! GroupReceive(fromRank)
  context.setReceiveTimeout(timeout) //optional
  become {
    case ReceiveTimeout => {
      //handle the timeout
      context.stop(self)
    }
    case res => {
      //do your thing with res, asynchronously
      context.stop(self)
    }
  }
}

//some other code which won't wait for the above operations

The latter version also creates a new temporary actor which sends the GroupReceive message and then waits for a reply, after which it kills itself.

The bottom line is that in order to receive a message from an actor you must be an actor yourself. Actors can't just send message to something other than an ActorRef.

So either you use the ask pattern which creates a temporary actor behind the scenes and manages this temporary actor's lifecycle itself, exposing to you just a nice simple future to work with, or you can create the temporary actor yourself, but then you have to manage its lifecycle (i.e. remember to kill it once it has done its job)

Choose the option that suits you best.


Related Query

More Query from same tag