Initializing an actor before being able to handle some other messages

前端 未结 2 394
野趣味
野趣味 2021-01-12 08:07

I have an actor which creates another one:

class MyActor1 extends Actor {
  val a2 = system actorOf Props(new MyActor(123))
}

The second ac

相关标签:
2条回答
  • 2021-01-12 08:35

    You could use become method to change actor's behavior after initialization:

    class MyActor2(a: Int) extends Actor {
    
      server ! GetInitializationData
    
      def initialize(d: InitializationData) = ???
    
      //handle incoming messages
      val initialized: Receive = {
        case "job1" => // do some job but after it's initialized (bootstrapped) itself
      }
    
      def receive = {
        case d @ InitializationData =>
          initialize(d)
          context become initialized
      }
    }
    

    Note that such actor will drop all messages before initialization. You'll have to preserve these messages manually, for instance using Stash:

    class MyActor2(a: Int) extends Actor with Stash {
    
      ...
    
      def receive = {
        case d @ InitializationData =>
          initialize(d)
          unstashAll()
          context become initialized
        case _ => stash()
      }
    }
    

    If you don't want to use var for initialization you could create initialized behavior using InitializationData like this:

    class MyActor2(a: Int) extends Actor {
    
      server ! GetInitializationData
    
      //handle incoming messages
      def initialized(intValue: Int, strValue: String): Receive = {
        case "job1" => // use `intValue` and `strValue` here
      }
    
      def receive = {
        case InitializationData(intValue, strValue) =>
          context become initialized(intValue, strValue)
      }
    }
    
    0 讨论(0)
  • 2021-01-12 08:55

    I don't know wether the proposed solution is a good idea. It seems awkward to me to send a Initialization message. Actors have a lifecycle and offer some hooks. When you have a look at the API, you will discover the prestart hook.

    Therefore i propose the following:

    • When the actor is created, its preStart hook is run, where you do your server request which returns a future.
    • While the future is not completed all incoming messages are stashed.
    • When the future completes it uses context.become to use your real/normal receive method.
    • After the become you unstash everything.

    Here is a rough sketch of the code (bad solution, see real solution below):

    class MyActor2(a: Int) extends Actor with Stash{
    
      def preStart = {
        val future = // do your necessary server request (should return a future)
        future onSuccess {
          context.become(normalReceive)
          unstash()
        }
      }
    
      def receive = initialReceive
    
      def initialReceive = {
        case _ => stash()
      }
    
      def normalReceive = {
        // your normal Receive Logic
      }
    }
    

    UPDATE: Improved solution according to Senias feedback

    class MyActor2(a: Int) extends Actor with Stash{
    
      def preStart = {
        val future = // do your necessary server request (should return a future)
        future onSuccess {
          self ! InitializationDone
        }
      }
    
      def receive = initialReceive
    
      def initialReceive = {
        case InitializationDone =>
          context.become(normalReceive)
          unstash()
        case _ => stash()
      }
    
      def normalReceive = {
        // your normal Receive Logic
      }
    
      case class InitializationDone
    }
    
    0 讨论(0)
提交回复
热议问题