2017-06-11 79 views
0

我有一個Future lazy val,它獲得一些提交未來操作的對象和函數。在創建的將來提交操作

class C { 
    def printLn(s: String) = println(s) 
} 
lazy val futureC: Future[C] = Future{Thread.sleep(3000); new C()} 
def func(s: String): Unit = { 
    futureC.foreach{c => c.printLn(s)} 
} 

問題是,當Future未完成時,它以相反的順序執行操作。因此,舉例來說,如果我執行sequentialy

func("A") 
func("B") 
func("C") 

未來完成

scala> C 
B 
A 

這個命令是對我很重要之後,我得到。有沒有辦法保存這個命令?

當然,我可以使用一個演員,在將來還沒有準備好的時候,他會要求未來和存儲字符串,但對我來說似乎是多餘的。

+2

執行的順序是隨機的,不僅僅是相反的。返回'A C B'給我。這取決於未來何時完成。如果執行必須是順序的,爲什麼要將其並行化。 – prayagupd

+0

@prayagupd只是假設「futureC」返回Future(實際上它是演員詢問結果),並且我不想在方法「func」的第一次調用時阻止。 – Igor

+0

@Igor,問題是,你在'func'的第一次調用中沒有阻塞。它恰好在最後完成。 – Dima

回答

0
lazy val futureC: Future[C] 

lazy val S IN階將到使用線程安全同步塊中的代碼進行編譯。

這裏,當調用func(A)時,它將獲得lazy val的鎖定,並且該線程將進入睡眠狀態。 因此func(B) & func(C)會被鎖鎖住。

當這些被阻止的線程運行時,命令不能得到保證。

如果你像下面那樣做,你會得到你想要的訂單。這是因爲for理解會創建一個flatMap,& map基於鏈的順序執行。

lazy val futureC: Future[C] = Future { 
    Thread.sleep(1000) 
    new C() 
    } 

    def func(s: String) : Future[Unit] = { 
    futureC.map { c => c.printLn(s) } 
    } 

    val x = for { 
    _ <- func("A") 
    _ <- func("B") 
    _ <- func("C") 
    } yield() 

即使沒有lazy關鍵字,訂單也會保留。除非真的有必要,否則您可以刪除lazy關鍵字。

希望這會有所幫助。

-1

您可以使用Future.traverse來確保執行順序。

這樣的事情..我不知道你的func如何引用正確的futureC,所以我把它移到裏面。

def func(s: String): Future[Unit] = { 
    lazy val futureC = Future{Thread.sleep(3000); new C()} 
    futureC.map{c => c.printLn(s)} 
} 

def traverse[A,B](xs: Seq[A])(fn: A => Future[B]): Future[Seq[B]] = 
    xs.foldLeft(Future(Seq[B]())) { (acc, item) => 
     acc.flatMap { accValue => 
     fn(item).map { itemValue => 
      accValue :+ itemValue 
     } 
     } 
    } 

traverse(Seq("A","B","C"))(func) 
+0

哦,我還沒有準備好字符串序列,如果我沒有問題在這裏。調用'func'程序中的某處,並且在未來還未完成時可能會再次調用它。我想收集字符串並在完成時將它們發送到未來,但如何完成這一點我不明白。 – Igor

+0

'.traverse'不能保證執行的順序,只是結果序列中的期貨的順序相同(完成可能仍然無序)。 – Dima

+0

哦,我的錯誤,謝謝。我習慣於不同的遍歷實現。病毒更新與保留的實現。 – Stephen