[英]For comprehension: how to run Futures sequentially
鑒於以下方法......
def doSomething1: Future[Int] = { ... }
def doSomething2: Future[Int] = { ... }
def doSomething3: Future[Int] = { ... }
......以及以下的理解:
for {
x <- doSomething1
y <- doSomething2
z <- doSomething3
} yield x + y + z
這三種方法並行運行,但在我的情況下, doSomething2
必須在doSomething1
完成后運行。 如何按順序運行這三種方法?
編輯
正如Philosophus42所建議的,下面是doSomething1
的可能實現:
def doSomething1: Future[Int] = {
// query the database for customers younger than 40;
// `find` returns a `Future` containing the number of matches
customerService.find(Json.obj("age" -> Json.obj("$lt" -> 40)))
}
...所以Future
是通過對另一個方法的內部調用創建的。
編輯2
也許我過分簡化了用例......我很抱歉。 讓我們再試一次,更接近真實的用例。 以下是三種方法:
for {
// get all the transactions generated by the exchange service
transactions <- exchange.orderTransactions(orderId)
//for each transaction create a log
logs <- Future.sequence(tansactions.map { transaction =>
for {
// update trading order status
_ <- orderService.findAndUpdate(transaction.orderId, "Executed")
// create new log
log <- logService.insert(Log(
transactionId => transaction.id,
orderId => transaction.orderId,
...
))
} yield log
})
} yield logs
我要做的是為與訂單關聯的每個事務創建一個日志。 即使transactions
只包含一個條目,也會多次調用logService.insert
。
首先, doSomethingX
中的代碼如何? 更令人討厭的是,使用您給定的代碼,期貨並行運行。
為了使Future
執行順序,只需使用
for {
v1 <- Future { ..block1... }
v2 <- Future { ..block2... }
} yield combine(v1, v2)
這個工作的原因是,語句Future {..body ..}啟動異步計算,在那個時間點評估語句。
隨着上面的理解,desugared
Future { ..block1.. }
.flatMap( v1 =>
Future { ..block>.. }
.map( v2 => combine(v1,v2) )
)
很明顯,那
Future{ ...block1... }
有結果, flatMap
方法被觸發,其中 Future { ...block2... }
。 因此Future { ...block2... }
在 Future { ...block1... }
之后執行
Future
Future {
<block>
}
通過ExecutionContext
立即觸發包含塊的ExecutionContext
小片1:
val f1 = Future { <body> }
val f2 = Future { <otherbody> }
這兩個計算是並行運行的(如果你的ExecutionContext
是這樣設置的),因為這兩個值是立即計算的。
摘錄2:
構造
def f1 = Future { ..... }
一旦f1
被調用,將開始執行未來
編輯:
j3d,我仍然感到困惑,為什么你的代碼沒有按預期工作,如果你的語句是正確的,那么Future 是在 computeSomethingX
方法中創建的。
這是一個代碼片段,證明, computeSomething2
是在computeSomething1
之后執行的
import scala.concurrent。{Await,Future} import scala.concurrent.duration._
object Playground {
import scala.concurrent.ExecutionContext.Implicits.global
def computeSomething1 : Future[Int] = {
Future {
for (i <- 1 to 10) {
println("computeSomething1")
Thread.sleep(500)
}
10
}
}
def computeSomething2 : Future[String] = {
Future {
for(i <- 1 to 10) {
println("computeSomething2")
Thread.sleep(800)
}
"hello"
}
}
def main(args: Array[String]) : Unit = {
val resultFuture: Future[String] = for {
v1 <- computeSomething1
v2 <- computeSomething2
} yield v2 + v1.toString
// evil "wait" for result
val result = Await.result(resultFuture, Duration.Inf)
println( s"Result: ${result}")
}
}
與輸出
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
Result: hello10
編輯2
如果您希望它們並行執行,請事先創建期貨(此處為f1
和f2
)
def main(args: Array[String]) : Unit = {
val f1 = computeSomething1
val f2 = computeSomething2
val resultFuture: Future[String] = for {
v1 <- f1
v2 <- f2
} yield v2 + v1.toString
// evil "wait" for result
val result = Await.result(resultFuture, Duration.Inf)
println( s"Result: ${result}")
}
我看到兩個變種來實現這個目標:
第一:確保期貨是在理解范圍內創建的。 這意味着你的函數應該像這樣定義: def doSomething1: Future[Int] = Future { ... }
。 在這種情況下,for comprehension應按順序執行Futures。
第二:在其他人開始之前使用你需要完成的Future的地圖功能:
doSomething1.map{ i =>
for {
y <- doSomething2
z <- doSomething3
} yield i + y + z
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.