在 Scala Akka 期货中,map 和 flatMap 有啥区别?

Posted

技术标签:

【中文标题】在 Scala Akka 期货中,map 和 flatMap 有啥区别?【英文标题】:In Scala Akka futures, what is the difference between map and flatMap?在 Scala Akka 期货中,map 和 flatMap 有什么区别? 【发布时间】:2011-10-05 00:34:46 【问题描述】:

在普通的 Scala 映射和 flatMap 中,flatMap 将返回一个扁平化到列表中的数据的迭代。 但是在 Akka 文档中,map 和 flatMap 似乎做了不同的事情?

http://akka.io/docs/akka/1.1/scala/futures.html

它说“通常这工作得很好,因为这意味着运行快速函数的开销很小。如果函数有可能花费大量时间来处理它可能会更好同时完成,为此我们使用 flatMap:"

val f1 = Future 
  "Hello" + "World"


val f2 = f1 flatMap x =>
  Future(x.length)


val result = f2.get()

有人可以解释一下 Akka 期货中的 map 和 flatMap 有什么区别吗?

【问题讨论】:

我认为这有助于更好地理解它 - 为什么我们使用 flatMap raichoo.blogspot.com/2011/07/… 【参考方案1】:

在“普通”Scala 中(如您所说),map 和 flatMap 与列表无关(例如检查 Option)。

Alexey 给了你正确的答案。现在,如果你想知道为什么我们需要两者,它允许在编写期货时使用漂亮的for 语法。给定类似的东西:

val future3 = for( x <- future1;
                   y <- future2 ) yield ( x + y )

编译器将其重写为:

val future3 = future1.flatMap( x => future2.map( y => x+y ) )

如果您遵循方法签名,您应该会看到表达式将返回 Future[A] 类型的内容。

假设现在只使用了 map,编译器可能会这样做:

val future3 = future1.map( x => future2.map( y => x+y ) )

但是,结果应该是Future[Future[A]] 类型。这就是为什么你需要把它弄平。

要了解背后的概念,这是我读过的最好的介绍之一:

http://www.codecommit.com/blog/ruby/monads-are-not-metaphors

【讨论】:

我没有意识到 flatten 可以展平类型,我的想法是它将对象列表展平为一个列表。因此,从我的“旧”想法来看,我认为它只是遍历列表并将其展平。实际上,正如您所说的那样,展平可以展平类型 Future[Future[A]] 类似于 List[List[A]]。一旦我做了这一步,我就能更好地理解它。 非常感谢!尤其是帖子!那个“分号”让我大吃一惊!【参考方案2】:

有人可以解释一下 Akka 期货中 map 和 flatMap 之间的区别吗?

类型,基本上:

flatMap[A](f: T => Future[A]): Future[A] 

map[A](f: T => A): Future[A] 

【讨论】:

确切地说,当您有一个返回 Future[A] 的方法,而不是获得 Future[Future[A]],您想要 Future[A] 时,这具有相关性。 如果你想了解更多围绕mapflatMap的设计原则,它们植根于Monad抽象,我推荐Scala的创造者Martin Oderskycoursera.org/learn/progfun2/lecture/98tNE/lecture-1-4-monads的这个讲座【参考方案3】:

我在这里粘贴这两种方法的实现。 英文术语的区别如下 并将函数的结果作为新的未来返回

         /** Creates a new future by applying a function to the successful result of
       *  this future. If this future is completed with an exception then the new
       *  future will also contain this exception.
       *
       *  $forComprehensionExamples
       */
      def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] =  // transform(f, identity)
        val p = Promise[S]()
        onComplete  v => p complete (v map f) 
        p.future
      

      /** Creates a new future by applying a function to the successful result of
       *  this future, and returns the result of the function as the new future.
       *  If this future is completed with an exception then the new future will
       *  also contain this exception.
       *
       *  $forComprehensionExamples
       */
      def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = 
        import impl.Promise.DefaultPromise
        val p = new DefaultPromise[S]()
        onComplete 
          case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
          case Success(v) => try f(v) match 
            // If possible, link DefaultPromises to avoid space leaks
            case dp: DefaultPromise[_] => dp.asInstanceOf[DefaultPromise[S]].linkRootOf(p)
            case fut => fut.onComplete(p.complete)(internalExecutor)
           catch  case NonFatal(t) => p failure t 
        
    p.future
   

从实现上看,区别在于 flatMap 实际上是在 promise 完成时调用带有结果的函数。

case Success(v) => try f(v) match 

阅读精彩文章:http://danielwestheide.com/blog/2013/01/16/the-neophytes-guide-to-scala-part-9-promises-and-futures-in-practice.html

【讨论】:

以上是关于在 Scala Akka 期货中,map 和 flatMap 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 scala 期货效率不高?

Scala并发框架Akka原理详解

使用 Scala/Akka 在 JVM 中进行高频交易

Scala框架Akka学习

Scala-Unit7-Scala并发编程模型AKKA

Scala笔记整理:Actor和AKKA