flatMap 一个 futures 列表的 Futures 正在执行阻塞操作,那么为什么不将代码包含在 blocking {..} 中呢?
Futures that flatMap a list of futures are doing a blocking operation, so why not enclose the code in blocking {..}?
在学习 Reactive Programming 上 Coursera class 的一些练习和视频时,我看到了 'sequence' List
期货方法的定义。方法 returns a 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)))
}
}
这个代码是老师给的,所以我猜它应该代表了如何做这种事情的最佳模式。然而,在讲座的其他地方,讲师说:
Whenever you have a long-running computation or blocking make sure to
run it inside the blocking construct. For example:
blocking {
Thread.sleep(1000)
}
is used to designate a piece of code as potentially blocking. An asynchronous computation that has a blocking construct is
typically scheduled in a separate thread to avoid potential deadlock.
Example: let's say you have a future f that waits on a timer or for a
resource or a monitor condition that can only be fulfilled by some
other future g. In that case, the part of the code in f that does the
waiting should be wrapped in the blocking, otherwise the future g
might never be run.
现在...我不明白的是为什么 'match' 表达式没有包含在 'blocking' 表达式中。难道我们不希望所有的 flatMapping(可能)花费大量时间吗?
旁注:scala.concurrent.Future class 中有一个 'official' 序列方法,该实现也不使用阻塞。
我也会 post 把这个发到 Coursera 论坛,如果我得到回复,我也会 post 在这里。
Don't we expect all the flatMapping to take (potentially) a noticeable chunk of time?
没有。 flatMap
只是立即构造一个新的 Future
和 returns。它不会阻塞。
参见 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
时发生的情况概述:
- 创建承诺
- 为这个未来添加回调
- Return 诺言
The method returns a Future
which will do the work of waiting on all the futures
我认为该描述有些误导。您从 Future.sequence
返回的 Future
并不是真正的 "do work"。正如你在上面的代码中看到的,你从 flatMap
得到的 Future
(因此你从 Future.sequence
得到的 Future
)只是一个最终会完成的承诺通过别的东西。唯一真正做任何事情的是 ExecutionContext
; Future
只是指定要做什么。
在学习 Reactive Programming 上 Coursera class 的一些练习和视频时,我看到了 'sequence' List
期货方法的定义。方法 returns a 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)))
}
}
这个代码是老师给的,所以我猜它应该代表了如何做这种事情的最佳模式。然而,在讲座的其他地方,讲师说:
Whenever you have a long-running computation or blocking make sure to run it inside the blocking construct. For example:
blocking { Thread.sleep(1000) }
is used to designate a piece of code as potentially blocking. An asynchronous computation that has a blocking construct is typically scheduled in a separate thread to avoid potential deadlock. Example: let's say you have a future f that waits on a timer or for a resource or a monitor condition that can only be fulfilled by some other future g. In that case, the part of the code in f that does the waiting should be wrapped in the blocking, otherwise the future g might never be run.
现在...我不明白的是为什么 'match' 表达式没有包含在 'blocking' 表达式中。难道我们不希望所有的 flatMapping(可能)花费大量时间吗?
旁注:scala.concurrent.Future class 中有一个 'official' 序列方法,该实现也不使用阻塞。
我也会 post 把这个发到 Coursera 论坛,如果我得到回复,我也会 post 在这里。
Don't we expect all the flatMapping to take (potentially) a noticeable chunk of time?
没有。 flatMap
只是立即构造一个新的 Future
和 returns。它不会阻塞。
参见 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
时发生的情况概述:
- 创建承诺
- 为这个未来添加回调
- Return 诺言
The method returns a
Future
which will do the work of waiting on all the futures
我认为该描述有些误导。您从 Future.sequence
返回的 Future
并不是真正的 "do work"。正如你在上面的代码中看到的,你从 flatMap
得到的 Future
(因此你从 Future.sequence
得到的 Future
)只是一个最终会完成的承诺通过别的东西。唯一真正做任何事情的是 ExecutionContext
; Future
只是指定要做什么。