9

我正在第一次在 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”操作(可能更多是上一个问题的后续)。

干杯。

4

3 回答 3

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 实例可以在不同的 ExecutionContext 上运行,那么部分 for-comprehension 可以在不同的线程池上运行。

于 2013-01-18T08:16:17.537 回答
2

我面临同样的问题......我发现这个关于理解的一般解释很有用。可能这有帮助:

为了理解

for -comprehension 是,和集合上的操作的语法糖。mapflatMapfilter

一般形式是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 表达式看起来像传统的 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)

这意味着您可以对自己的类型使用 for-comprehension,只要您定义了 map、flatMap 和 filter

示例 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)))
于 2016-02-22T23:17:39.400 回答
2

您在Arun Manivannan的“ Scala notes – Futures – 3 (Combinators and Async) ” 中也有一个很好的并发Future执行示例。

我们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

平面图

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]or Future[String]or Future[AwesomeClass]- 你不能只有一个普通的Future.
    一个花哨的术语是type-constructor
    相比之下, aList是一个类型构造函数(也是一个 Monad)。
    AList是类型Int, String或任何其他类型的值的容器。没有包含类型的List/Future不存在。
  2. FuturehasflatMapunitfunctions(因此也是一个map函数)。
于 2016-06-14T19:00:36.073 回答