如果你不阻挡未来,它会导致像种族一样的竞争吗?

时间:2015-12-10 18:56:25

标签: scala playframework future

假设我有一个未来调用的控制器,然后我重定向到另一个期望该调用已经计算的页面。是否有可能未来的回报速度不够快,当我重定向到另一页时,数据将不会是新鲜的"?

示例:

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吗?

如果我希望调用已经计算,我必须阻止。那么这个没有硬性规定吗?

2 个答案:

答案 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,一旦您有了这个要求,就可以通过formap,{在flatMap签名中,{1}}等错误处理变得更加清晰。额外的好处是,如果您现在有阻止呼叫,可能在将来您可以找到在该服务中使用的异步API,您的签名不必更改。

要从可能失败的阻止调用中返回Future,最好的策略是用Future包装它们。但是,如果您计算的数据不能失败,则使用future {}是更好的选择。

答案 1 :(得分:0)

当您的代码成立时,您的action1将启动将来执行的代码,然后将重定向发送到浏览器。无法保证将来的代码将在调用action2之前完成。

不管怎么说,假设这是play框架,你可以等待将来的代码完成而不会阻塞当前线程,通过使用Future上的map或onComplete方法来注册回调,你可以在其中完成请求发送重定向。您需要将Action更改为Action.async。

相关问题