你的问题有几处有点切题,所以我会依次解决
1) 重定向响应会在未来返回之前返回吗?
无法判断,而且它的行为也不会始终如一。对于在 Future 完成其计算之前不返回的重定向,您应该使用异步操作,以便在 Future 完成之前调用不会响应重定向:
def action1 = Action.async { request =>
futureComputation().map { _ => Redirect(routes.HomeController.action2) }
}
2) 我的服务层是否应该返回Future?
简单的答案是是的,如果底层 API 也是非阻塞的。
更细微的答案是是的,即使您的服务调用被阻塞,但您的工作流程中使用的所有其他调用都会返回一个未来。第二个条件是关于构图和可读性。
假设工作流程是:
findById
-
fetchHttpResource(异步获取)
-
update(带有来自 fetch 的数据
因为只有一个异步组件,工作流应该是异步的,你可以这样写:
def workflow(id:UUID):Future[Unit] = {
val user = UserService.findById(id)
fetchHttpResource(id).map { resource =>
val updatedUser = user.copy(x = resource)
UserService.update(updatedUser)
}
}
如果 UserService 也返回 Future,您可以使用 for-comprehension 代替:
def workflow(id: UUID): Future[Unit] = for {
user <- UserService.findById(id)
resource <- fetchHttpResource(id)
_ <- UserService.update(user.copy(x = resource))
} yield {}
这让我想到了
3) 是否有使用Future 的硬性规定?
异步代码是一个乌龟一直向下的事情。一旦你的链中有一个异步调用,你的链必须返回一个Future,一旦你有这个要求,通过for、map、flatMap等组成Future 签名中的错误处理变得更加清晰。额外的好处是,如果您现在有一个阻塞调用,也许将来您可以在该服务中找到一个异步 API 来使用,您的签名不必更改。
要从可能失败的阻塞调用中返回Future,最好的策略是用future {} 包装它们。但是,如果您计算的数据不会失败,则使用Future.successful() 是更好的选择。