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 时发生的情况概述:

  1. 创建承诺
  2. 为这个未来添加回调
  3. 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 只是指定要做什么。