在Reactive Programming上完成Coursera课程的一些练习和视频时,我看到了一种方法的定义,即'序列'List
期货。该方法返回Future
,它将执行等待fts
中所有未来的工作(请参阅下面的代码),并将这些结果打包在List[T]
中,Future[List[T]]
将在{def sequence[T](fts: List[Future[T]]): Future[List[T]] = {
fts match {
case Nil => Future(Nil)
case (ft::fts) => ft.flatMap(t => sequence(fts)
.flatMap(ts => Future(t::ts)))
}
}
时可用序列返回的1}}已完成。
blocking {
Thread.sleep(1000)
}
这段代码是由教师给出的,所以我猜它应该代表如何做这种事情的最佳模式。然而,在讲座的其他地方,教师们说:
每当你有长时间运行的计算或阻塞时,请确保 在阻塞构造中运行它。例如:
CloudConfigurationManager
用于将一段代码指定为可能阻塞。具有阻塞构造的异步计算是 通常安排在一个单独的线程中,以避免潜在的死锁。 示例:假设您有一个等待计时器或等待的未来f 资源或监视条件,只能由某些人来履行 其他未来g。在那种情况下,f中的代码部分就是这样做的 等待应该在封锁中包裹,否则将来g 可能永远不会被运行。
现在......我不明白为什么'match'表达式没有包含在'blocking'表达式中。难道我们不希望所有flatMapping(可能)花费大量时间吗?
附注:scala.concurrent.Future类中有一个“官方”序列方法,该实现也不使用阻塞。
我也会将它发布到Coursera论坛,如果我得到回复,我也会在这里发帖。
答案 0 :(得分:4)
难道我们不希望所有flatMapping都(可能)花费大量时间吗?
不。 flatMap
只构建一个新的Future
并立即返回。它不会阻止。
见the default implementation of flatMap
。这是它的简化版本:
trait Future[+T] {
def flatMap[S](f: T => Future[S])
(implicit executor: ExecutionContext): Future[S] = {
val promise = new Promise[S]()
this.onComplete {
// The first Future (this) failed
case Failure(t) => promise.failure(t)
case Success(v1) =>
// Apply the flatMap function (f) to the first Future's result
Try(f(v1)) match {
// The flatMap function (f) threw an exception
case Failure(t) => promise.failure(t)
case Success(future2) =>
future2.onComplete {
// The second Future failed
case Failure(t) => promise.failure(t)
// Both futures succeeded - Complete the promise
// successfully with the second Future's result.
case Success(v2) => promise.success(v2)
}
}
}
promise.future
}
}
致电flatMap
时会发生什么情况:
该方法返回
Future
,它将完成等待所有期货的工作
我认为这种描述有些误导。您从Future
返回的Future.sequence
并没有“真正起作用”。正如您在上面的代码中看到的那样,您从Future
获得的flatMap
(以及您从Future
获得的Future.sequence
只是一个最终将完成的承诺通过别的东西。唯一真正做到的是ExecutionContext
; Future
只是指定要做什么。