Ich habe den folgenden Code, der eine Antwort an den Absender-Akteur zurücksendet (der antwortende Akteur lädt mit Slick eine Liste aus einer Tabelle):

class ManageUsersData extends Actor {

  def receive = {
    case _       => {
      sender ! loadUsers
    }
  }


  def loadUsers =  {

    var list = new ListBuffer[String]()
    val db = Database.forConfig("dbconfig")
    try {

      val users: TableQuery[Users] = TableQuery[Users]
      val future = db.run(users.result)


      future onComplete {
        case Success(u) => u.foreach {
          user => {
            list += user.firstName
          }
          list
        }
        case Failure(t) => println("An error has occured: " + t.getMessage)
      }

    } finally db.close

   list

  }
}

Das Problem hierbei ist, dass loadUsers zurückkehrt, bevor auf den Abschluss der Zukunft gewartet wird. Wie kann das angegangen werden?

1
ps0604 30 Dez. 2015 im 18:08

3 Antworten

Beste Antwort

Sie sollten das Rohrmuster verwenden:

import akka.pattern.pipe

// ...

def receive = {
  val originalSender = sender
  loadUsers pipeTo originalSender
}
3
Jean Logeart 30 Dez. 2015 im 15:18

Die konzeptionelle Lösung wird bereits von Jean Logeart erwähnt. In Bezug auf loadUsers denke ich, dass dies eine kürzere Version ist?

def loadUsers =  {

    val db = Database.forConfig("dbconfig")
    try {

      val users: TableQuery[Users] = TableQuery[Users]
      db.run(users.result).map(_.firstName)

    } catch {
      case e: Exception => println("An error has occured: " + e.getMessage)
    } finally db.close

 }
3
Ashalynd 30 Dez. 2015 im 15:34

Aus meiner Sicht wäre es am einfachsten, das future einfach an das sender zurückzusenden, anstatt an das asynchron gefüllte list.

Wie in

  def loadUsers =  {

    val db = Database.forConfig("dbconfig")
    try {

      val users: TableQuery[Users] = TableQuery[Users]
      
      val future = db.run(users.result)

      future.map {     //the future
        _.map {        //the returning Seq
          _.firstName 
        }
      }

    } finally db.close

  }

Jetzt hat der Anrufer die Last, die Zukunft oder den Fehler zu bewältigen.

Dies hat auch den Nachteil, dass, wenn sender die Operation ask / ? verwendet, das asynchrone Ergebnis ein Future ist, das ein weiteres Future umschließt.

Sie können dieses Problem mithilfe der Methode pipeTo beheben, die eine zukünftige Nachricht an den Anrufer sendet, ohne sich darum kümmern zu müssen, sie zu entpacken.

Der Nachteil von piping ist, dass das sender eine Möglichkeit haben sollte, zu identifizieren, welche Antwort zu welcher Anforderung gehört. Eine mögliche Lösung besteht darin, eine Anforderungskennung zu senden, die mit der Antwort zurückgesendet wird, damit der anfordernde Akteur die beiden leicht verknüpfen kann.

Randnotiz

Warum sollten Sie im zukünftigen Ergebnis das Attribut firstName zuordnen, anstatt eine Projektion in der Slick-Abfrage zu verwenden? Ich nehme an, es ist, um das Beispiel einfach zu halten.

2
Community 20 Juni 2020 im 09:12