Scala 我可以将Action.async与多个未来一起使用吗?

Scala 我可以将Action.async与多个未来一起使用吗?,scala,playframework,future,Scala,Playframework,Future,最后,我得到了关于在PlayFramework中使用Scala Futures的建议,谢谢。现在事情变得有点复杂了。比如说,在我绘制水果的地图之前: def getMapData(coll: MongoCollection[Document], s: String): Future[Seq[Document]] = ... def mapFruit(collection: MongoCollection[Document]) = Action.async { val fut = getMa

最后,我得到了关于在PlayFramework中使用Scala Futures的建议,谢谢。现在事情变得有点复杂了。比如说,在我绘制水果的地图之前:

def getMapData(coll: MongoCollection[Document], s: String): Future[Seq[Document]] = ...

def mapFruit(collection: MongoCollection[Document]) = Action.async {
  val fut = getMapData(collection, "fruit")
  fut.map { docs: Seq[Document] =>
    Ok(docs.toJson)
  } recover {
    case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
  }
}
事实证明,人们更关心苹果而不是香蕉或樱桃,因此,如果地图上出现的物品不超过100件,人们希望苹果优先于香蕉和樱桃,但地图上出现的物品中苹果的比例不超过一定比例。某些函数
pickDocs
确定正确的混合。我想这样的事情可能会管用,但不会:

def mapApplesBananasCherries(collection: MongoCollection[Document]) = Action.async {
  val futA = getMapData(collection, "apples")
  val futB = getMapData(collection, "bananas")
  val futC = getMapData(collection, "cherries")
  futA.map { docsA: Seq[Document] =>
    futB.map { docsB: Seq[Document] =>
      futC.map { docsC: Seq[Document] =>
        val docsPicked = pickDocs(100, docsA, docsB, docsC)
        Ok(docsPicked.toJson)
      }
    }
    // won't compile without something here, e.g. Ok("whatever")
  } recover {
    case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
  }
}

当我只有一个未来时,生活很简单,但现在我有三个未来。我该怎么做才能使它(1)起作用(2)再次变得简单?在这三种未来都有价值之前,我无法真正构建web响应。

基本上,您应该使用flatMap

futA.flatMap { docsA: Seq[String] =>
  futB.flatMap { docsB: Seq[String] =>
    futC.map { docsC: Seq[String] =>
      docsPicked = pickDocs(100, docsA, docsB, docsC)
        Ok(docsPicked.toJson)
      }
    }
}
此外,您还可以使用以下内容进行理解:

val res = for {
  docsA <- futA
  docsB <- futB
  docsC <- futC
} yield Ok(pickDocs(100, docsA, docsB, docsC).toJson)
res.recover {
  case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
}
val res=for{

docsA基本上,你应该使用平面图

futA.flatMap { docsA: Seq[String] =>
  futB.flatMap { docsB: Seq[String] =>
    futC.map { docsC: Seq[String] =>
      docsPicked = pickDocs(100, docsA, docsB, docsC)
        Ok(docsPicked.toJson)
      }
    }
}
此外,您还可以使用以下内容进行理解:

val res = for {
  docsA <- futA
  docsB <- futB
  docsC <- futC
} yield Ok(pickDocs(100, docsA, docsB, docsC).toJson)
res.recover {
  case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
}
val res=for{

docsA这是期货和“包含价值”的类似类别的一种非常常见的模式(例如,
选项
列表

若要组合要使用的结果,请使用该方法和生成的代码

def mapApplesBananasCherries(collection: MongoCollection[Document]) = Action.async {
  val futA = getMapData(collection, "apples")
  val futB = getMapData(collection, "bananas")
  val futC = getMapData(collection, "cherries")
  futA.flatMap { docsA =>
    futB.flatMap { docsB =>
      futC.map { docsC =>
        val docsPicked = pickDocs(100, docsA, docsB, docsC)
        Ok(docsPicked.toJson)
      }
    }
  } recover {
    case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
  }
}
事实上,它非常常见,存在一种特殊的语法来提高可读性,这需要理解:下面的代码相当于前面的代码片段

def mapApplesBananasCherries(collection: MongoCollection[Document]) = Action.async {
  val futA = getMapData(collection, "apples")
  val futB = getMapData(collection, "bananas")
  val futC = getMapData(collection, "cherries")
  for {
    apples <- futA
    bananas <- futB
    cherries <- futC
  } yield {
    val docsPicked = pickDocs(100, apples, bananas, cherries)
    Ok(docsPicked.toJson)
  } recover {
    case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
  }
}
def mapapplesbanascherries(集合:MongoCollection[Document])=Action.async{
val futA=getMapData(收集,“苹果”)
val futB=getMapData(收集,“香蕉”)
val futC=getMapData(收集,“樱桃”)
为了{

苹果这是期货和“包含价值”的类似类别的一种非常常见的模式(例如
选项
列表

若要组合要使用的结果,请使用该方法和生成的代码

def mapApplesBananasCherries(collection: MongoCollection[Document]) = Action.async {
  val futA = getMapData(collection, "apples")
  val futB = getMapData(collection, "bananas")
  val futC = getMapData(collection, "cherries")
  futA.flatMap { docsA =>
    futB.flatMap { docsB =>
      futC.map { docsC =>
        val docsPicked = pickDocs(100, docsA, docsB, docsC)
        Ok(docsPicked.toJson)
      }
    }
  } recover {
    case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
  }
}
事实上,它非常常见,存在一种特殊的语法来提高可读性,这需要理解:下面的代码相当于前面的代码片段

def mapApplesBananasCherries(collection: MongoCollection[Document]) = Action.async {
  val futA = getMapData(collection, "apples")
  val futB = getMapData(collection, "bananas")
  val futC = getMapData(collection, "cherries")
  for {
    apples <- futA
    bananas <- futB
    cherries <- futC
  } yield {
    val docsPicked = pickDocs(100, apples, bananas, cherries)
    Ok(docsPicked.toJson)
  } recover {
    case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
  }
}
def mapapplesbanascherries(集合:MongoCollection[Document])=Action.async{
val futA=getMapData(收集,“苹果”)
val futB=getMapData(收集,“香蕉”)
val futC=getMapData(收集,“樱桃”)
为了{

苹果如果我的理解是您希望以该优先级执行苹果、樱桃和香蕉,那么我将对其进行类似的编码

import scala.concurrent.{Await, Future}
import scala.util.Random
import scala.concurrent.duration._

object WaitingFutures extends App {

  implicit val ec = scala.concurrent.ExecutionContext.Implicits.global

  val apples = Future {50 + Random.nextInt(100)}
  val cherries = Future {50 + Random.nextInt(100)}
  val bananas =  Future {50 + Random.nextInt(100)}

  val mix = for {
    app <- apples
    cher <- if (app < 100) cherries else Future {0}
    ban <- if (app + cher < 100) bananas else Future {0}
  } yield (app,cher,ban)


  mix.onComplete {m =>
    println(s"mix ${m.get}")
  }

  Await.result(mix, 3 seconds)

}
导入scala.concurrent.{wait,Future}
导入scala.util.Random
导入scala.concurrent.duration_
对象等待应用程序{
隐式val ec=scala.concurrent.ExecutionContext.Implicits.global
val apples=Future{50+Random.nextInt(100)}
val cherries=未来{50+随机.nextInt(100)}
val香蕉=未来{50+随机.nextInt(100)}
val mix=用于{

app如果我的理解是,您希望以该优先级执行苹果、樱桃和香蕉,那么我会编写类似于此的代码

import scala.concurrent.{Await, Future}
import scala.util.Random
import scala.concurrent.duration._

object WaitingFutures extends App {

  implicit val ec = scala.concurrent.ExecutionContext.Implicits.global

  val apples = Future {50 + Random.nextInt(100)}
  val cherries = Future {50 + Random.nextInt(100)}
  val bananas =  Future {50 + Random.nextInt(100)}

  val mix = for {
    app <- apples
    cher <- if (app < 100) cherries else Future {0}
    ban <- if (app + cher < 100) bananas else Future {0}
  } yield (app,cher,ban)


  mix.onComplete {m =>
    println(s"mix ${m.get}")
  }

  Await.result(mix, 3 seconds)

}
导入scala.concurrent.{wait,Future}
导入scala.util.Random
导入scala.concurrent.duration_
对象等待应用程序{
隐式val ec=scala.concurrent.ExecutionContext.Implicits.global
val apples=Future{50+Random.nextInt(100)}
val cherries=未来{50+随机.nextInt(100)}
val香蕉=未来{50+随机.nextInt(100)}
val mix=用于{

app这不会编译,因为嵌套的future块正在返回一个
future[future[Response]]]
。如果改为在futures上使用
flatMap
,您的futures将不会嵌套

如果您希望此操作的重复性稍低,可以使用
Future.sequence
来同时启动期货。您可以使用模式匹配来重新提取列表:

val futureCollections = List("apples", "bananas", "cherries").map{ getMapData(collection, _) }

Future.sequence(futureCollections) map { case docsA :: docsB :: docsC :: Nil =>
  Ok(pickDocs(100, docsA, docsB, docsC).toJson)
} recover {
  case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
}
或者,您可以将一个列表列表(按优先级排序)交给
pickDocs
函数,供其选择

Future.sequence(futureCollections) map { docLists =>
  Ok(pickDocs(docLists, 100, 0.75f).toJson)
} recover {
  case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
}
pickDocs
实现将占列表头的一个百分比,除非完整列表中没有足够的文档(需要更多文档),然后递归地将相同的百分比应用于剩余的插槽列表



这不会编译,因为嵌套的future块正在返回一个
future[future[Response]]]
。如果改为在futures上使用
flatMap
,则不会嵌套futures

如果您希望此操作的重复性稍低,可以使用
Future.sequence
来同时启动期货。您可以使用模式匹配来重新提取列表:

val futureCollections = List("apples", "bananas", "cherries").map{ getMapData(collection, _) }

Future.sequence(futureCollections) map { case docsA :: docsB :: docsC :: Nil =>
  Ok(pickDocs(100, docsA, docsB, docsC).toJson)
} recover {
  case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
}
或者,您可以将一个列表列表(按优先级排序)交给
pickDocs
函数,供其选择

Future.sequence(futureCollections) map { docLists =>
  Ok(pickDocs(docLists, 100, 0.75f).toJson)
} recover {
  case e => Console.err.println("FAIL: " + e.getMessage); BadRequest("FAIL")
}
pickDocs
实现将占列表头的一个百分比,除非完整列表中没有足够的文档(需要更多文档),然后递归地将相同的百分比应用于剩余的插槽列表



我认为他指的是并行化未来的计算,即使使用a来理解它也不会产生结果,直到futc完成
val futA=getMapData(集合,“苹果”);val futB=getMapData(集合,“香蕉”);val futc=getMapData(集合,“樱桃”)
创造了这些未来,因此并行计算已经开始,理解只是对这些未来结果的平面图。谢谢你,伊戈尔,这非常清楚。谢谢路易斯和纳德!我认为他指的是并行化未来计算,即使使用理解,也不会产生,直到futc完成d
val futA=getMapData(集合,“苹果”);val futB=getMapData(集合,“香蕉”);val futC=getMapData(集合,“樱桃”)
创建了这些未来,因此并行计算已经开始,为了理解这些未来的结果,只需要平面图。谢谢你,伊戈尔,这非常清楚。谢谢你,路易斯和纳德!不完全相同:在t