假设我有一个未来调用的控制器,然后我重定向到另一个期望该调用已经计算的页面。是否有可能未来的回报速度不够快,当我重定向到另一页时,数据将不会是新鲜的"?
示例:
class HomeController {
def action1 = Action{
val f1 = Future { ... }
Redirect(routes.HomeController.action2)
}
def action2 = Action {
// expects the call to f1:Future to have executed
}
}
我问的原因是我的服务层应该返回Future还是阻止?或者我应该将Future传递给我的控制器中的调用代码。
UserService
save
delete
update
findById
etc. etc..
这些应该归还Future吗?
如果我希望调用已经计算,我必须阻止。那么这个没有硬性规定吗?
答案 0 :(得分:3)
你的问题中有几件事情有些相反,所以我会依次解决这些问题
1)重定向响应会在将来返回之前返回吗?
不可能告诉,而且,它不会表现得一致。如果重定向在Future
完成计算之前未返回,则应使用异步操作,以便在Future
完成之前调用不会响应重定向:
def action1 = Action.async { request =>
futureComputation().map { _ => Redirect(routes.HomeController.action2) }
}
2)我的服务层应该返回Future
吗?
简单的答案是是,如果底层API也是非阻塞的。
更细致的答案是是,即使您的服务呼叫是阻止的,但工作流程中使用的每个其他呼叫都会返回。第二个资格是关于构图和可读性。
假设工作流程为:
findById
fetchHttpResource
(async get)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
签名中,{1}}等错误处理变得更加清晰。额外的好处是,如果您现在有阻止呼叫,可能在将来您可以找到在该服务中使用的异步API,您的签名不必更改。
要从可能失败的阻止调用中返回Future
,最好的策略是用Future
包装它们。但是,如果您计算的数据不能失败,则使用future {}
是更好的选择。
答案 1 :(得分:0)
当您的代码成立时,您的action1将启动将来执行的代码,然后将重定向发送到浏览器。无法保证将来的代码将在调用action2之前完成。
不管怎么说,假设这是play框架,你可以等待将来的代码完成而不会阻塞当前线程,通过使用Future上的map或onComplete方法来注册回调,你可以在其中完成请求发送重定向。您需要将Action更改为Action.async。