Scala期货和承诺回调基础知识

时间:2015-08-26 13:38:41

标签: scala asynchronous concurrency promise future

我是Scala的初学者Futures / Promises

我正在尝试使用Futures(使用回调)执行此操作:

  • 获取可用的Git存储库。
  • 获取每个存储库的贡献者。
  • 获取每个存储库的README文件。
  • 最后,每个存储库创建一个包含存储库URL,贡献者和自述文件的GitData响应。

以下是代码:

// Final write container to hold the result.
val promise = Promise[List[GitData]]()

val repositoriesF = Future {
  GitDriver.repositoriesOf(request.user)
}

val gitRepositories = Promise[List[GitRepository]]()

repositoriesF onSuccess {
  case repositories => {
    val contributors = Promise[List[Contributor]]()
    val contributorsF = Future(repositories.map(GitDriver.contributors))
    val readMe = Promise[List[String]]
    val readMeF = Future(repositories.map(GitDriver.readme))

    contributorsF.onSuccess {
      case contrib => contributors.success(contrib.flatten)
    }

    readMeF.onSuccess {
      case r => readMe.success(r)
    }

    val extractedContributors = contributors.future.value
    val extractedReadme = readMe.future.value
    println(extractedContributors.size)
    println(extractedReadme.size)
  }
}

Await.ready(repositoriesF, Duration.Inf)

贡献者和自述文件的大小始终为零。

我解决这个问题的方法是,我可以通过Futures将贡献者和自述文件的提取平行化,因为它们不相互依赖。他们只需要一个存储库对象。

我在这里缺少什么?我确信有更多优雅的解决方案用于理解,地图,dsls等。但只是好奇地走到这一个的底部!感谢。

3 个答案:

答案 0 :(得分:2)

你正在做一些奇怪的事情。根据您的问题,主要问题是您要在此块中打印值:

repositoriesF onSuccess {
   case repositories => {
      ...

所以从某种意义上说,你只是在等待FIRST异步计算,并且只为你需要的其他信息设置回调(自述文件和贡献者)。在设置回调之后,那些没有完成,你没有结果。

第二个奇怪的事情是你在第一个未来做Await()而不做其他计算。您很可能希望等待所有信息都可用。

你正在做的第三个奇怪的事情是使用太多的承诺。你不想在获得某些结果时使用Promise,但是当你必须将Future返回给其他人并且需要一种方法来完成未来,当你不再能够访问未来时。

至于如何解决你的问题,我会用理解链接期货:

val futureGitData = for {
  repos <- Future { GitDriver.repositoriesOf(request.user) }
  repo <- repos
  readme <- Future { GitDriver.readme(repo) } 
  contributors <- Future { GitDriver.contributors(repo) } 
} yield (repo, readme, contributors)

请注意,这有更好的并行性,因为我为每个存储库创建了不同的未来。你在做什么,Future(repositories.map(GitDriver.readme))会同步地做地图并返回一个未来。

请注意,我在此设备中没有Scala,因此代码可能不完美,但应该给你一个想法。

答案 1 :(得分:0)

目前您打印贡献者和自述文件的大小时,未来可能尚未完成,因此contributors.future.value将返回无。

您可以尝试这样的事情:

val repositoriesF = Future {
  GitDriver.repositoriesOf(request.user)
}

val contributorsAndReadmeFuture = for {
  repositories <- repositoriesF
  contributors <- Future(repositories.map(GitDriver.contributors))
  readme <- Future(repositories.map(GitDriver.readme))
} yield (contributors, readme)

contributorsAndReadmeFuture onSuccess {
  case (constributors, readme) => 
    println(constributors.size)
    println(readme.size)
}

答案 2 :(得分:0)

您的future.valueprintln行在Futures之前执行(Future(...)的明确行和Promise.future中设置的onSuccess是完成。

Future的值仍为None,与之相比:

val p = Promise[Int]()
p.future.value
// Option[scala.util.Try[Int]] = None
p.future.value.size
// Int = 0

由于您希望继续使用您的解决方案,您可以执行类似的操作(没有多余的Promises):

repositoriesF onSuccess {
  case repositories =>
    val contributorsF = Future(repositories.flatMap(GitDriver.contributors))
    val readMeF = Future(repositories.map(GitDriver.readme))

    contributorsF zip readMeF onSuccess {
      case (contrib, readme) => 
        println(contrib.size)
        println(readme.size)
    }
}