2016-03-02 90 views
1

我有一个这样的端点:嵌套的异步调用

POST /user/:id/addData 

而且控制器功能如下:

def addData(id: Int) = Action.async { implicit request => 

    // AsynC#1 - Make sure this user exists 
    usersDAO.get(id).map(user => { 
     is(user.isEmpty) { 
      BadRequest("That user doesn't exist") 
     } else { 

      val body = request.body.asJson.get.as[JsObject] 
      // Data processing here ... 

      // AsynC#2 - Insert some data from the POST body 
      (for { 
       foo <- fooDAO.insert(fooData) 
       bar <- barDAO.insert(barData) 
      } yield (foo, bar)).map { 
       case options => Ok("Data was added!") 
      }.recover { // <-------------------------- Compilation error here 
       case e => BadRequest(e) 
      } 
     } 
    }) 
} 

我得到一个编译时错误:

type mismatch; 
found : scala.concurrent.Future[play.api.mvc.Result] 
required: play.api.mvc.Result 

我相信这个错误是因为执行上下文在第一个异步调用(又名一个未来)中,所以,因为我正在输入另一个异步调用,就像我返回嵌套的期货一样。

这样做的正确方法是什么?如果可能的话,我想取消这些调用的嵌套(如Promises in Javascript)。

回答

2

你的问题是非常接近的一个在下面的答案解释:

https://stackoverflow.com/a/35640546/4600

基本上,你是映射Future但回到你的map内部的两个不同的类型:

if(user.isEmpty) { 
    BadRequest("That user doesn't exist") 
} 

上面的if块返回Result,而else块返回Future[Result]。但要求它返回Result,以便map将产生Future[Result]而不是Future[Future[Result]]。现在

,这是非常简单的解决:

  1. 首先,由于您使用的是map内异步调用(导致Future),你不应该使用map,但flatMap(请参见回答下面链接到更多细节)。
  2. flatMap中的所有块必须返回Future[Result]

这里,我们去(见注释):

def addData(id: Int) = Action.async { implicit request => 

    // See that we are now using a flatMap 
    usersDAO.get(id).flatMap(user => { 
    if(user.isEmpty) { 
     // Return a future instead of a Result 
     Future.successful(BadRequest("That user doesn't exist")) 
    } else { 

     val body = request.body.asJson.get.as[JsObject] 

     // Just to be more explicity about the types and to 
     // be easier to made comments below. 
     val future: Future[(Foo, Bar)] = for { 
     foo <- fooDAO.insert(fooData) 
     bar <- barDAO.insert(barData) 
     } yield (foo, bar) 

     // This map returns a Future[Result] which is exactly what 
     // out flatMap about expects. 
     future.map { 
     case options => Ok("Data was added!") 
     }.recover { // This also returns a Future[Result] and now the compiler is happy 
     case e => BadRequest(e) 
     } 
    } 
    }) 
} 
1

你的if/else试图返回既是ResultFuture[Result]map内,当这Future#map需要一个Result。完成这项工作的最简单方法是将usersDAO.get(id).map { ...更改为usersDAO.get(id).flatMap { ...并将BadRequest("That user doesn't exist")换成Future.successful(...)

由于所有的这些方法似乎回到期货,可以让这个更优雅通过把它们放在一个换理解:

def addData(id: Int) = Action.async(parse.json) { implicit request => 
    (for { 
     user <- usersDAO.get(id).filter(_.nonEmpty) 
     body = request.body.as[JsObject] 
     foo <- fooDAO.insert(fooData) 
     bar <- barDAO.insert(barData) 
    } yield { 
     // user, body, foo, bar are in scope here 
     Ok("Data was added!") 
    }) recover { 
     case _: NoSuchElementException => BadRequest("That user doesn't exist") 
     case e => BadRequest(e) 
    } 
} 

这可能不是编译向右走,因为我不能肯定你所有的返回类型是什么。请注意,我还添加了BodyParserparse.json,以便您可以只写request.body