簡體   English   中英

Scala:管理期貨順序執行的好方法嗎?

[英]Scala: Cool way to manage sequential execution of Futures?

我正在嘗試在Scala中編寫數據模塊。

在並行加載整個數據時,某些數據依賴於其他數據,因此必須高效地管理執行順序。

例如在代碼中,我保留了一個帶有數據名稱和清單的地圖

val dataManifestMap = Map(
  "foo" -> manifest[String],
  "bar" -> manifest[Int],
  "baz" -> manifest[Int],
  "foobar" -> manifest[Set[String]], // need to be executed after "foo" and "bar" is ready
  "foobarbaz" -> manifest[String], // need to be executed after "foobar" and "baz" is ready
)

這些數據將存儲在可變的哈希圖中

private var dataStorage = new mutable.HashMap[String, Future[Any]]()

有一些代碼可以加載數據

def loadAllData(): Future[Unit] = {
  Future.join(
    (dataManifestMap map {
      case (data, m) => loadData(data, m) } // function has all the string matching and loading stuff
    ).toSeq
  )    
}

def loadData[T](data: String, m: Manifest[T]): Future[Unit] = {
  val d = data match {
    case "foo" => Future.value("foo")
    case "bar" => Future.value(3)
    case "foobar" => // do something with dataStorage("foo") and dataStorage("bar")
    ... // and so forth (in a real example it would be much more complicated for sure)
  }

  d flatMap { 
    dVal => { this.synchronized { dataStorage(data) = dVal }; Future.value(Unit) }
  }
}

這樣,當“ foo”和“ bar”准備就緒時,我無法確定是否加載了“ foobar”,依此類推。

由於我可能擁有數百個不同的數據,我該如何以一種“酷”的方式進行管理?

如果我可以擁有某種數據結構,其中包含有關某物的所有信息,則這些東西必須在某物之后加載,這將是“很棒的”,而FlatMap可以以一種簡潔的方式來處理順序執行。

我在這里先向您的幫助表示感謝。

在所有條件都相同的情況下,我傾向於將其for理解。 例如:

def findBucket: Future[Bucket[Empty]] = ???
def fillBucket(bucket: Bucket[Empty]): Future[Bucket[Water]] = ???
def extinguishOvenFire(waterBucket: Bucket[Water]): Future[Oven] = ???
def makeBread(oven: Oven): Future[Bread] = ???
def makeSoup(oven: Oven): Future[Soup] = ???
def eatSoup(soup: Soup, bread: Bread): Unit = ???


def doLunch = {
  for (bucket <- findBucket;
       filledBucket <- fillBucket(bucket);
       oven <- extinguishOvenFire(filledBucket);
       soupFuture = makeSoup(oven);
       breadFuture = makeBread(oven);
       soup <- soupFuture;
       bread <- breadFuture) {
    eatSoup(soup, bread)
  }
}

這將期貨鏈接在一起,並在滿足依賴性后調用相關方法。 請注意,我們在for理解中使用=允許我們同時啟動兩個Futures 就目前而言, doLunch返回Unit ,但是如果將以下幾行替換為:

// ..snip..
       bread <- breadFuture) yield {
    eatSoup(soup, bread)
    oven
  }
}

然后它將返回Future[Oven] -如果您想在午餐后將烤箱用於其他用途,這可能會很有用。

至於您的代碼,盡管我首先想到的是應該考慮使用Spray緩存 ,因為它看起來可能符合您的要求。 如果沒有,我的下一個想法將是使用基於類型化方法調用的內容替換當前使用的Stringly類型化接口:

private def save[T](key: String)(value: Future[T]) = this.synchronized {
  dataStorage(key) = value
  value
}

def loadFoo = save("foo"){Future("foo")}
def loadBar = save("bar"){Future(3)}
def loadFooBar = save("foobar"){
  for (foo <- loadFoo;
       bar <- loadBar) yield foo + bar // Or whatever
}
def loadBaz = save("baz"){Future(200L)}
def loadAll = {
  val topLevelFutures = Seq(loadFooBar, loadBaz)
  // Use standard library function to combine futures
  Future.fold(topLevelFutures)(())((u,f) => ())
}

// I don't consider this method necessary, but if you've got a legacy API to support...
def loadData[T](key: String)(implicit manifest: Manifest[T]) = {
  val future = key match {
      case "foo" => loadFoo
      case "bar" => loadBar
      case "foobar" => loadFooBar
      case "baz" => loadBaz
      case "all" => loadAll
  }
  future.mapTo[T]
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM