Scala:管理期货顺序执行的酷方法?

Scala:管理期货顺序执行的酷方法?,scala,future,Scala,Future,我正在尝试用Scala编写一个数据模块 在并行加载整个数据时,一些数据依赖于其他数据,因此必须以有效的方式管理执行序列 例如,在代码中,我保留了一个带有数据和清单名称的映射 val dataManifestMap = Map( "foo" -> manifest[String], "bar" -> manifest[Int], "baz" -> manifest[Int], "foobar" -> manifest[Set[String]], // nee

我正在尝试用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可以以一种简洁的方式处理顺序执行,那将是“很棒的”


提前感谢您的帮助。

在所有条件相同的情况下,我倾向于使用
进行理解。例如:

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)
  }
}
然后它将返回
Future[coven]
——如果您想在午餐后用烤箱做其他事情,这可能会很有用

至于你的代码,我的第一个应该是你应该考虑的,因为它看起来可能符合你的要求。如果没有,我的下一个想法将是替换您当前拥有的接口,并使用基于类型化方法调用的内容:

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]
}
private def save[T](键:字符串)(值:Future[T])=this.synchronized{
数据存储(键)=值
价值
}
def loadFoo=save(“foo”){Future(“foo”)}
def loadBar=save(“bar”){Future(3)}
def loadFooBar=保存(“foobar”){
对于(foo loadFoo)
案例“bar”=>加载条
案例“foobar”=>loadFooBar
案例“baz”=>loadBaz
案例“全部”=>loadAll
}
future.mapTo[T]
}

在所有条件相同的情况下,我倾向于使用
来理解。例如:

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)
  }
}
然后它将返回
Future[coven]
——如果您想在午餐后用烤箱做其他事情,这可能会很有用

至于你的代码,我的第一个将是你应该考虑的,因为它看起来可能符合你的要求。如果不是,我下一个想法是替换你当前得到的接口,并根据基于类型化方法调用的东西:

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]
}
private def save[T](键:字符串)(值:Future[T])=this.synchronized{
数据存储(键)=值
价值
}
def loadFoo=save(“foo”){Future(“foo”)}
def loadBar=save(“bar”){Future(3)}
def loadFooBar=保存(“foobar”){
对于(foo loadFoo)
案例“bar”=>加载条
案例“foobar”=>loadFooBar
案例“baz”=>loadBaz
案例“全部”=>loadAll
}
future.mapTo[T]
}

可能正在使用Actors会更切中要害吗?很抱歉使用了糟糕的sudo代码。另外,我需要的是确保在尝试加载“foobar”时“foo”和“bar”已准备就绪。因此,解决方案不一定需要与期货的顺序执行相关。它可以正确地使用承诺,也可以使用任何可以达到目的的东西。谢谢。这正是有状态的参与者可以发挥作用的地方。@Ashalynd谢谢你的建议!我仍然不确定应该使用什么样的设计:)也许在这里使用演员会更切中要害?对不起,sudo代码太糟糕了。另外,我所需要的是确保在尝试加载“foobar”时“foo”和“bar”已准备就绪。因此,解决方案不一定需要与期货的顺序执行相关。它可以正确地使用承诺,也可以使用任何可以达到目的的东西。谢谢。这正是有状态的参与者可以发挥作用的地方。@Ashalynd谢谢你的建议!我仍然不确定应该使用什么样的设计:)谢谢你的回答。我仍然不确定我是否应该实现您的设计或使用演员…也许您可以告诉我们更多关于您的问题的细节。例如,任务序列是否需要在运行时进行修改(可能由非技术用户修改),或者如果在编译时进行了修改,是否可以?是否可以通过检查数据来确定正确的顺序?我们实际上谈论的是什么样的数据,加载数据时使用的是什么样的代码?这些东西可能暗示了一种特定的设计。不,它不必是可修改的。顺序将是固定的。虽然,除了在开始时加载整个数据外,“foobar”也有可能在运行时单独重新加载(数据取决于其他数据),因此在这种情况下,在请求加载“foobar”时,必须事先加载“foo”和“bar”。2.检查数据对确定订单3没有帮助。数据将由Int、Map等组成。数据将从redis、MySQL和msgpack缓存数据加载。此时,所有代码都是用Scala编写的。(详细说明1)loadData[T]可以单独调用,而不仅仅是在loadAllData中。因此,当调用loadData(“foobar”)并且没有加载“foo”和“bar”时,我们应该在加载“foobar”之前同时加载“foo”和“bar”!希望这有帮助!这听起来有点像缓存。这样公平吗?你会考虑使用喷雾缓存之类的东西吗?谢谢你的回答。我仍然不确定我是否应该实现您的设计或使用演员…也许您可以告诉我们更多关于您的问题的细节。例如,任务序列是否需要在运行时进行修改(可能由非技术用户修改),或者如果在编译时进行了修改,是否可以?是否可以通过检查数据来确定正确的顺序?我们实际上谈论的是什么样的数据,加载数据时使用的是什么样的代码?这些东西可能暗示了一种特定的设计。不,它不必是可修改的。顺序将是固定的。虽然,除了在开始时加载整个数据外,“foobar”也有可能在运行时单独重新加载(数据取决于其他数据),因此在这种情况下,在请求加载“foobar”时,必须事先加载“foo”和“bar”。2.检查数据对确定订单3没有帮助。数据将