2017-07-04 2 views
0

Ich habe eine Frage, die Scoping in Scala betrifft. Ich habe eine Funktion wie folgt aus:Scala Timing Probleme mit dem Hinzufügen von Zeug zu einem Seq

def getElements(id: Int): Seq[Element] = { 
    var test = "" 
    dto.getElementIds(id).map { 
     elementIds => { 
     test += " hello " 
     elementIds.foreach(elementId => dto.getElement(elementId).map { 
      case Some(element) => test += " hi " 
       println("ThirdPrint: " + test) 
     }) 
     println("SecondPrint: " + test) 
     } 
    } 
    println("FirstPrint: " + test) 
} 

Lets "elementsIds" sagen eine Länge von 2. hat die Konsole sagt: firstprint: second: hallo ThirdPrint: hallo hallo ThirdPrint: hallo hallo hallo Warum macht Scala das? Ich würde annehmen, dass der dritte Druck zuerst ausgeführt wird. Als ich zur "FirstPrint" komme, sind die "Hi" s verschwunden. Warum wird die letzte Codezeile zuerst ausgeführt? Ich arbeite mit Slick und Futures, hat das etwas damit zu tun? Vielen Dank!

UPDATE

Danke, funktioniert gut. Ist es möglich, stattdessen einen Seq zurückzugeben? Wie folgt:

def getElements(id: Int): Future[Seq[Element]] = { 
    var mySequence: Seq[Element] = Seq() 
    val elementsIds: Future[Seq[Int]] = dto.getElementIds(id) 
    var test = "" 
    val elementsF = elementsIds.flatMap { 
     elementIds => { 
     test += " hello " 
     val idsAsElements: Seq[Future[Element]] = elementIds.map(elementId => dto.getElement(elementId).collect { 
      case Some(element) => mySequence = mySequence :+ element 
     }) 
     val idsAsElementsF: Future[Seq[Element]] = Future.sequence(idsAsElements) 
     idsAsElementsF.onComplete(_ => println("SecondPrint: " + test)) 
     idsAsElementsF 
     } 
    } 
    elementsF.onComplete(_ => println("FirstPrint: " + test)) 
    elementsF 
    } 

Ist es möglich, "mySequence" zurückzugeben, wenn idsAsElements "onComplete" ist?

+0

Ist 'dto.getElementIds (id)' 'eine Future' Rückkehr? Können Sie die Typen bereitstellen? –

+0

ja Future [Seq [Int]] (ids) – Felix

+0

Es läuft also in einem parallelen Thread im Hintergrund. Während Ihr 'FirstPrint' in Zeile ausgeführt wird, arbeitet der andere Thread immer noch daran, diese IDs von dort zu bekommen, wo er sie bekommt, sobald dies geschieht, wird die' .map' ausgeführt. – Dima

Antwort

1
object X { 
    import scala.concurrent.ExecutionContext.Implicits.global 
    case class Element() 
    object dto{ 

    def getElementIds(i: Int): Future[Seq[Int]] = Future(Seq(1,2,3)) 
    def getElement(i: Int): Future[Option[Element]] = Future(Some(Element())) 

    } 

    def main(args: Array[String]): Unit = { 
    getElements(0) 
    Thread.sleep(10000) // waiting logs 
    } 
    def getElements(id: Int): Future[Seq[Element]] = { 
    val elementsIds: Future[Seq[Int]] = dto.getElementIds(id) 
    var test = "" 
    val elementsF = elementsIds.flatMap { 
     elementIds => { 
     test += " hello " 
     val idsAsElements: Seq[Future[Element]] = elementIds.map(elementId => dto.getElement(elementId).collect { 
      case Some(element) => test += " hi " 
      println("ThirdPrint: " + test) 
      element 
     }) 
     val idsAsElementsF: Future[Seq[Element]] = Future.sequence(idsAsElements) 
     idsAsElementsF.onComplete(_ => println("SecondPrint: " + test)) 
     idsAsElementsF 
     } 
    } 
    elementsF.onComplete(_ => println("FirstPrint: " + test)) 
    elementsF 
    } 
} 

Ausgabe:

ThirdPrint: hallo hallo

ThirdPrint: hallo hallo hallo

ThirdPrint: hallo hallo hallo hallo

second: hallo hallo hallo hallo

FirstPrint: hallo hallo hallo hi

+0

Vergessen Sie nicht, wenn Sie .map verwenden müssen Sie alle Fälle abdecken .map {Fall Einige (a) => ...} failed on none – Ivan

+0

Möchten Sie von der Funktion die ersten abgeschlossenen idsAsElements erhalten und andere überspringen? – Ivan

+0

Ich möchte den Rückgabewert (Some (element)) wie folgt anhängen: 'mySequence = mySequence: + element' from 'validsAsElements: Seq [Zukünftige [Element]] = elementIds.map (elementId => dto.getElement (elementId) .collect {...}' und gebe diese Liste zurück, nachdem ich fertig bin. – Felix

1

Ja, das ist keine gute Lösung. Sie sollten vermeiden, in Ihrem Code zu blockieren, außer vielleicht auf der höchstmöglichen Ebene (main Methode). Auch ist der veränderliche Zustand schlecht, insbesondere wenn er mit Nebenläufigkeit (Futures) kombiniert wird.

Ihre Funktion sollte Future zurückgeben. So etwas funktioniert (Ich bin mir nicht sicher, ob ich die Absicht Ihres Codes richtig vermute - Ihre Funktion wurde zurückgegeben, aber geschrieben, um eine Unit zurückzugeben ... Ich nehme an, dass das, was Sie wirklich zurückgeben wollten, das Ergebnis war von getElement Anrufe für jeden id):

def getElements(id: Int): Future[Seq[Element]] = dto 
    .getElementIds(id) 
    .map { ids => ids.map(dto.getElement) } 
    .flatMap(Future.sequence) 
    .map(_.flatten) 

ich Ausdrucke entfernt, da nicht sicher war, was der Zweck war sie dienen (da die Anrufe dto.getElement auch parallel geschieht, ist es nicht klar, wo und in welcher Reihenfolge diese Strings gedruckt werden sollen.

Sie könnten Ihre "erwartete Ausgabe", simulieren, indem eine weitere Transformation am Ende zum Beispiel das Hinzufügen:

.andThen { case Success(results) => 
    val str = results.foldLeft("hello") { case (a,b) => 
     println("ThirdPrint: " + a + " hi") 
     a + " hi" 
    } 
    println("SecondPrint: " + str) 
    println("FirstPrint: " + str 
    }