2016-03-02 99 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