Scala Futures如何与flatMap链接在一起?

时间:2013-01-18 07:53:56

标签: scala monads future

我正在Scala第一次使用Futures,正在研究使用flatMap组合器的例子;我一直在关注这个讨论:

http://docs.scala-lang.org/overviews/core/futures.html

具体来说,这个例子:

val usdQuote = future { connection.getCurrentValue(USD) }
val chfQuote = future { connection.getCurrentValue(CHF) }
val purchase = for {
    usd <- usdQuote
    chf <- chfQuote
      if isProfitable(usd, chf)
} yield connection.buy(amount, chf)

purchase onSuccess {
    case _ => println("Purchased " + amount + " CHF")
}

被翻译成:

val purchase = usdQuote flatMap {
    usd =>
         chfQuote
        .withFilter(chf => isProfitable(usd, chf))
        .map(chf => connection.buy(amount, chf))
}

我有点麻烦抓到的是如何以及何时执行flatMap?

据我所知,usdQuote和chfQuote是在“某个时间”由“某个线程”执行的,并且他们注册的回调函数被调用,问题是:

a)usdQuote和chfQuote是否同时执行? (我很确定他们是。)

b)flatMap如何将Future useQuote的值分配给usd?如同在操作usdQuote完成时调用它?

c)执行'flatMap'和'map'操作的线程(可能是上一个问题的后续操作)。

干杯。

3 个答案:

答案 0 :(得分:10)

  • a)当你创建它们时,你已经开始对范围内的隐式ExecutionContext执行它们,因此它们可能同时运行,因为它取决于执行它们的方式。

  • b)它并没有真正分配这样的值,但实现使用onComplete方法使得一旦达到结果就会触发您传递的函数。目前,这应链接到我所指的flatMap方法:https://github.com/scala/scala/blob/v2.11.2/src/library/scala/concurrent/Future.scala#L246

  • c)这些是通过前面提到的ExecutionContext运行的,还要考虑如果这些Future实例可以在不同的ExecutionContexts上运行,那么for-comprehension的部分可以在不同的线程池上运行。

答案 1 :(得分:2)

我面临着同样的问题......我发现了有关复合的有用this一般性解释。可能有帮助:

对于-悟

对于集合的mapflatMapfilter操作,for-comprehension是语法糖

一般表格为for (s) yield e

  • s是一系列生成器和过滤器
  • p <- e是一个生成器
  • if f是一个过滤器
  • 如果有多个生成器(相当于嵌套循环),则最后一个生成器的变化速度比第一个
  • 如果您想使用多行而不需要分号,则可以使用{ s }代替( s )
  • e是生成的集合的元素

示例1:

  // list all combinations of numbers x and y where x is drawn from
  // 1 to M and y is drawn from 1 to N
  for (x <- 1 to M; y <- 1 to N)
    yield (x,y)

相当于

(1 to M) flatMap (x => (1 to N) map (y => (x, y)))

翻译规则

for-expression看起来像传统的for循环,但内部工作方式不同

  • for (x <- e1) yield e2已翻译为e1.map(x => e2)
  • for (x <- e1 if f) yield e2已翻译为for (x <- e1.filter(x => f)) yield e2
  • for (x <- e1; y <- e2) yield e3已翻译to e1.flatMap(x => for (y <- e2) yield e3)

这意味着只要您定义map,flatMap和filter

,就可以对自己的类型使用for-understanding。

示例2:

for {  
  i <- 1 until n  
  j <- 1 until i  
  if isPrime(i + j)  
} yield (i, j)

相当于

for (i <- 1 until n; j <- 1 until i if isPrime(i + j))
    yield (i, j)

相当于

(1 until n).flatMap(i => (1 until i).filter(j => isPrime(i + j)).map(j => (i, j)))

答案 2 :(得分:2)

您还可以在&#34; Scala notes – Futures – 3 (Combinators and Async)&#34;中有一个很好的并发Future执行示例。来自 Arun Manivannan

  

我们的Futures需要并行运行   为了实现这一点,我们需要做的就是提取Future块并单独声明它们。

代码:

val oneFuture: Future[Int] = Future {
  Thread.sleep(1000)
  1
}

val twoFuture: Future[Int] = Future {
  Thread.sleep(2000)
  2
}

val threeFuture: Future[Int] = Future {
  Thread.sleep(3000)
  3
}

作为-理解

def sumOfThreeNumbersParallelMapForComprehension(): Future[Int] = for {  
    oneValue <- oneFuture
    twoValue <- twoFuture
    threeValue <- threeFuture
} yield oneValue + twoValue + threeValue

<强> flatmap

def sumOfThreeNumbersParallelMap(): Future[Int] = oneFuture.flatMap { oneValue =>  
    twoFuture.flatMap { twoValue =>
      threeFuture.map { threeValue =>
        oneValue + twoValue + threeValue
      }
    }
}

测试:

describe("Futures that are executed in parallel") {
  it("could be composed using for comprehensions") {
    val futureCombinators = new FutureCombinators
    val result = timed(Await.result(futureCombinators.sumOfThreeNumbersParallel(), 4 seconds))
      result shouldBe 6
  }
}

它确实说明了:

  
      
  1. Future是某种类型的值的容器(即它接受一个类型作为参数,没有它就不能存在)。
      您可以拥有Future[Int]Future[String]Future[AwesomeClass] - 您不能只拥有简单Future
      一个奇特的术语是类型构造函数   要进行比较,List是一个类型构造函数(以及Monad)   ListInt, String或任何其他类型的值的容器。不存在包含类型的List / Future
  2.   
  3. Future具有flatMapunit个函数(以及相应的map函数)。
  4.