scala 期货 - 地图与平面地图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31641190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Futures - map vs flatmap
提问by mattinbits
I've read the docs about mapand flatMapand I understand that flatMapis used for an operation that accepts a Futureparameter and returns another Future. What I don't fully understand is why I would want to do this. Take this example:
我已经阅读了关于mapand的文档flatMap,我知道它flatMap用于接受一个Future参数并返回另一个Future. 我不完全理解的是我为什么要这样做。拿这个例子:
- User hits my webservice asking to "do stuff"
- I download a file (which is slow)
- I process the file (which is CPU intensive)
- Render the result
- 用户点击我的网络服务要求“做事”
- 我下载了一个文件(很慢)
- 我处理文件(这是 CPU 密集型的)
- 渲染结果
I understand that I would want to use a future to download the file but I have have two options re processing it:
我知道我想使用 future 来下载文件,但我有两个选项可以重新处理它:
val downloadFuture = Future {/* downloadFile */}
val processFuture = downloadFuture map {/* processFile */}
processFuture onSuccess { case r => renderResult(r) }
or
或者
val downloadFuture = Future {/* download the file */}
val processFuture = downloadFuture flatMap { Future {/* processFile */} }
processFuture onSuccess { case r => renderResult(r) }
By adding debug statements (Thread.currentThread().getId) I see that in both cases download, processand renderoccur in the same thread (using ExecutionContext.Implicits.global).
通过添加调试语句 ( Thread.currentThread().getId),我看到在这两种情况下都下载process并render出现在同一线程中(使用ExecutionContext.Implicits.global)。
Would I use flatMapsimply to decouple downloadFileand processFileand ensure that processFilealways runs in a Futureeven if it was not mapped from downloadFile?
我是否会flatMap简单地使用来解耦downloadFile并processFile确保它processFile始终在 a 中运行,Future即使它没有从 映射downloadFile?
采纳答案by Peter Neyens
ensure that
processFilealways runs in aFutureeven if it was not mapped fromdownloadFile?
确保
processFile始终在 a 中运行,Future即使它没有从downloadFile?
Yes that is correct.
对,那是正确的。
However most of the time you wouldn't use Future { ... }directly, you would use functions (from other libraries or your own) which return a Future.
但是,大多数情况下您不会Future { ... }直接使用,而是使用返回Future.
Imagine the following functions :
想象一下以下功能:
def getFileNameFromDB{id: Int) : Future[String] = ???
def downloadFile(fileName: String) : Future[java.io.File] = ???
def processFile(file: java.io.File) : Future[ProcessResult] = ???
You could use flatMapto combine them :
你可以flatMap用来组合它们:
val futResult: Future[ProcessResult] =
getFileNameFromDB(1).flatMap( name =>
downloadFile(name).flatMap( file =>
processFile(file)
)
)
Or using a for comprehension :
或使用 a for comprehension :
val futResult: Future[ProcessResult] =
for {
name <- getFileNameFromDB(1)
file <- downloadFile(name)
result <- processFile(file)
} yield result
Most of the time you would not call onSuccess(or onComplete). By using one of these functions you register a callback function which will be executed when the Futurefinishes.
大多数时候你不会打电话onSuccess(或onComplete)。通过使用这些函数之一,您可以注册一个回调函数,该函数将在Future完成时执行。
If in our example you would like to render the result of the file processing, you would return something like Future[Result]instead of calling futResult.onSuccess(renderResult). In the last case your return type would be Unit, so you can not really return something.
如果在我们的示例中您想呈现文件处理的结果,您将返回类似于Future[Result]而不是调用futResult.onSuccess(renderResult). 在最后一种情况下,您的返回类型将是Unit,因此您无法真正返回某些内容。
In Play Framework this could look like :
在 Play Framework 中,这可能如下所示:
def giveMeAFile(id: Int) = Action.async {
for {
name <- getFileNameFromDB(1)
file <- downloadFile(name)
processed <- processFile(file)
} yield Ok(processed.byteArray).as(processed.mimeType))
}
回答by mattinbits
If you have a future, let's say, Future[HttpResponse], and you want to specify what to do with that result when it is ready, such as write the body to a file, you may do something like responseF.map(response => write(response.body). However if writeis also an asynchronous method which returns a future, this mapcall will return a type like Future[Future[Result]].
如果你有一个未来,比方说,Future[HttpResponse]并且你想在它准备好时指定如何处理该结果,例如将主体写入文件,你可以执行类似responseF.map(response => write(response.body). 但是,如果write它也是一个返回未来的异步方法,则此map调用将返回类似Future[Future[Result]].
In the following code:
在以下代码中:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val numF = Future{ 3 }
val stringF = numF.map(n => Future(n.toString))
val flatStringF = numF.flatMap(n => Future(n.toString))
stringFis of type Future[Future[String]]while flatStringFis of type Future[String]. Most would agree, the second is more useful. Flat Map is therefore useful for composing multiple futures together.
stringF是类型Future[Future[String]]whileflatStringF是类型Future[String]。大多数人会同意,第二个更有用。因此,平面地图对于将多个期货组合在一起很有用。
When you use forcomprehensions with Futures, under the hood flatMapis being used together with map.
当您for在 Futures 中使用推导式时,引擎盖flatMap下正在与map.
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val threeF = Future(3)
val fourF = Future(4)
val fiveF = Future(5)
val resultF = for{
three <- threeF
four <- fourF
five <- fiveF
}yield{
three * four * five
}
Await.result(resultF, 3 seconds)
This code will yield 60.
此代码将产生 60。
Under the hood, scala translates this to
在引擎盖下,scala 将其转换为
val resultF = threeF.flatMap(three => fourF.flatMap(four => fiveF.map(five => three * four * five)))
回答by Dhiraj Himani
def flatMap[B](f: A => Option[B]): Option[B] =
this match {
case None => None
case Some(a) => f(a)
}
This is a simple example where how the flatMap works for Option, this can help to understand better, It is actually composing it is not adding a wrapper again.That's what we need.
这是一个简单的例子,说明了 flatMap 如何为 Option 工作,这可以帮助更好地理解,它实际上是在组合它而不是再次添加包装器。这就是我们需要的。
回答by linehrr
Failure possibility from transformation function another reason why there exists flatMapon Future.
来自转换函数的失败可能性flatMap是 Future存在的另一个原因。
say you have a f: Future[T]. and a transformation func: T => B. however this func could fail due to some reason. so we want to indicate the caller it has failed.
说你有一个f: Future[T]. 和转型func: T => B。但是,由于某种原因,此功能可能会失败。所以我们想表明它失败的调用者。
with just Future.mapit's not obvious how to achieve this. but with flatMapyou can. because with flatMapit takes Future as return, and you can then easily do Future.failed(e) to bubble up the error to the caller. or if it succeeded, you can then use Future.success(r) to return the result.
只是Future.map如何实现这一点并不明显。但flatMap你可以。因为flatMap它需要 Future 作为回报,然后您可以轻松地执行 Future.failed(e) 将错误冒泡给调用者。或者,如果成功,则可以使用 Future.success(r) 返回结果。
aka. turn func into func: T => Future[B]
又名。把 func 变成func: T => Future[B]
this is very useful when you are chaining operations with Future and operations could fail in the middle.
当您使用 Future 链接操作并且操作可能在中间失败时,这非常有用。

