如果你不阻止未来,它会导致类似比赛的情况吗?
If you don't block on a future, can it cause a race like condition?
假设我有一个有未来调用的控制器,然后我重定向到另一个页面,该页面希望计算该调用。有没有可能未来 return 不够快,当我重定向到另一个页面时,数据不会 "fresh"?
示例:
class HomeController {
def action1 = Action{
val f1 = Future { ... }
Redirect(routes.HomeController.action2)
}
def action2 = Action {
// expects the call to f1:Future to have executed
}
}
我问的原因是我的服务层 return 应该是 Future 还是 block?或者我应该像在我的控制器中那样将 Future 传递给调用代码。
UserService
save
delete
update
findById
etc. etc..
这些return未来应该吗?
如果我希望调用已经计算,我必须阻止。那么这有没有硬性规定呢?
当您的代码生效时,您的 action1 将启动将来执行的代码,然后将重定向发送到浏览器。不能保证以后的代码会在调用 action2 之前完成。
但是,假设这是 play 框架,您可以等待未来的代码完成而不会阻塞当前线程,方法是使用 Future 上的 map 或 onComplete 方法注册回调,您将在其中完成通过发送重定向请求。您需要将操作更改为 Action.async.
你的问题中有几处有点离题,所以我会依次解决它们
1) 重定向响应return会不会在以后的return之前?
无法判断,而且它的行为不会始终如一。对于在 Future
完成其计算之前重定向到 not return,您应该使用异步操作,以便在 Future
完成之前调用不会响应重定向:
def action1 = Action.async { request =>
futureComputation().map { _ => Redirect(routes.HomeController.action2) }
}
2) 我的服务层 return 应该是 Future
吗?
简单的回答是是的,如果底层API也是非阻塞的。
更微妙的答案是 是的,即使您的服务调用是阻塞的,但您工作流中使用的每个其他调用 return 都是未来 。这第二个资格是关于组成和可读性的。
假设工作流程是:
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 returns 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
是否有硬性规定?
异步代码是一个乌龟一路向下的事情。一旦你的链中有一个异步调用,你的链 有 到 return 一个 Future
一旦你有这个要求,通过 for
组合, map
、flatMap
等,错误处理在 Future
签名中变得更加清晰。额外的好处是,如果您现在有一个阻塞调用,也许将来您可以找到一个异步 API 在该服务中使用,您的签名不必更改。
对于 return 阻止可能失败的调用的 Future
,最好的策略是用 future {}
包装它们。但是,如果您计算的数据不会失败,则使用 Future.successful()
是更好的选择。
假设我有一个有未来调用的控制器,然后我重定向到另一个页面,该页面希望计算该调用。有没有可能未来 return 不够快,当我重定向到另一个页面时,数据不会 "fresh"?
示例:
class HomeController {
def action1 = Action{
val f1 = Future { ... }
Redirect(routes.HomeController.action2)
}
def action2 = Action {
// expects the call to f1:Future to have executed
}
}
我问的原因是我的服务层 return 应该是 Future 还是 block?或者我应该像在我的控制器中那样将 Future 传递给调用代码。
UserService
save
delete
update
findById
etc. etc..
这些return未来应该吗?
如果我希望调用已经计算,我必须阻止。那么这有没有硬性规定呢?
当您的代码生效时,您的 action1 将启动将来执行的代码,然后将重定向发送到浏览器。不能保证以后的代码会在调用 action2 之前完成。
但是,假设这是 play 框架,您可以等待未来的代码完成而不会阻塞当前线程,方法是使用 Future 上的 map 或 onComplete 方法注册回调,您将在其中完成通过发送重定向请求。您需要将操作更改为 Action.async.
你的问题中有几处有点离题,所以我会依次解决它们
1) 重定向响应return会不会在以后的return之前?
无法判断,而且它的行为不会始终如一。对于在 Future
完成其计算之前重定向到 not return,您应该使用异步操作,以便在 Future
完成之前调用不会响应重定向:
def action1 = Action.async { request =>
futureComputation().map { _ => Redirect(routes.HomeController.action2) }
}
2) 我的服务层 return 应该是 Future
吗?
简单的回答是是的,如果底层API也是非阻塞的。
更微妙的答案是 是的,即使您的服务调用是阻塞的,但您工作流中使用的每个其他调用 return 都是未来 。这第二个资格是关于组成和可读性的。
假设工作流程是:
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 returns 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
是否有硬性规定?
异步代码是一个乌龟一路向下的事情。一旦你的链中有一个异步调用,你的链 有 到 return 一个 Future
一旦你有这个要求,通过 for
组合, map
、flatMap
等,错误处理在 Future
签名中变得更加清晰。额外的好处是,如果您现在有一个阻塞调用,也许将来您可以找到一个异步 API 在该服务中使用,您的签名不必更改。
对于 return 阻止可能失败的调用的 Future
,最好的策略是用 future {}
包装它们。但是,如果您计算的数据不会失败,则使用 Future.successful()
是更好的选择。