2017-07-26 68 views
0

我仍然在學習Scala所以這可能是一個易於回答的問題,但我一直停留在一遍又一遍地寫一個方法幾乎一天,無法得到這個代碼進行編譯。檢查錯誤/休息

我玩播放框架和反應蒙戈模板學習如何Scala和播放工作。 我有一個控制器與幾個方法,REST服務的端點。

的問題是關於下述方法,它接受JSON對象的列表,並使用該反應性蒙戈駕駛員更新這些對象。所述類具有一個部件,citiesFuture這是Future[JSONCollection]類型。 這我加入這個方法的原始類的代碼可以在這裏找到適合情境:CityController on github

def updateAll() = Action.async(parse.json) { request => 
    Json.fromJson[List[City]](request.body) match { 
    case JsSuccess(givenCities, _) => 
     citiesFuture onComplete[Future[Result]] { cities => 
     val updateFutures: List[Future[UpdateWriteResult]] = for { 
      city <- givenCities 
     } yield cities.get.update(City.getUniqueQuery(city), Json.obj("$set" -> city)) 

     val promise: Promise[Result] = Promise[Result] { 
      Future.sequence(updateFutures) onComplete[Result] { 
      case [email protected](_) => 
       var count = 0 
       for { 
       updateWriteResult <- s.value 
       } yield count += updateWriteResult.n 
       promise success Ok(s"Updated $count cities") 
      case Failure(_) => 
       promise success InternalServerError("Error updating cities") 
      } 
     } 
     promise.future 
     } 
    case JsError(errors) => 
     Future.successful(BadRequest("Could not build a city from the json provided. " + Errors.show(errors))) 
    } 
} 

我已經成功地走到這一步有很多的試驗和錯誤,但我開始明白怎麼有些Scala和期貨的機制的工作,我覺得:)我認爲我接近,但我的IDE仍然給我一個檢驗錯誤只是在單結束大括號行promise.future以上。

錯誤消息:類型單元的表達式不符合預期類型Nothing。 我已經檢查了Promise和onComplete代碼塊的預期返回值,但我不認爲他們期望Nothing作爲返回類型。

可能有人請向我解釋什麼,我缺少的,而且,我敢肯定,這是可以做的更好,所以讓我知道你是否有我可以學習任何提示!

+1

爲什麼在已經有'Future'的地方使用'Promise.future'?似乎沒有道理。 – cchantep

+0

因爲行動需要一個'未來[結果]'我認爲這是唯一的方法,構建一個新的未來完成所有更新後完成。 – semvdwal

回答

1

你就是在正確的軌道上,但@cchantep說,一旦你在Future-land經營,需要創建自己的Promise.future是非常不尋常的。

此外,看到onComplete正在被使用 - 慣用Scala通常傾向於對期貨的「更高級」抽象。我會嘗試證明我怎麼會寫你的函數在一個遊戲控制器:

首先的「端點」只是需要照顧一件事 - 接口與外部世界 - 即JSON的解析部分。如果一切正常轉換,它會調用實際執行工作的私有方法(performUpdateAll):

def updateAll() = Action.async(parse.json) { request => 
    Json.fromJson[List[City]](request.body) match { 
    case JsSuccess(givenCities, _) => 
     performUpdateAll(givenCities) 
    case JsError(errors) => 
     Future.successful(BadRequest("Could not build a city from the json provided. ")) 
    } 
} 

接下來,我們必須執行多個城市的更新私有函數。再一次,試圖遵守Single Responsibility Principle(在功能上 - 一個功能應該做一件事),我已經提取出updateCity,它知道如何更新恰好一個城市並返回Future[UpdateWriteResult]。這個好的副作用是代碼重用;你可能會發現你可以在其他地方使用這樣的功能。

private def performUpdateAll(givenCities:List[City]):Future[Result] = { 

    val updateFutures = givenCities.map { city => 
    updateCity(city) 
    } 

    Future.sequence(updateFutures).map { listOfResults => 
    if (listOfResults.forall(_.ok)) { 
     val count = listOfResults.map(_.n).sum 
     Ok(s"Updated $count cities")   
    } else { 
     InternalServerError("Error updating cities") 
    } 
    } 
} 

據我所知,這將在完全相同的方式工作,你打算你的工作。但通過使用Future.map而不是其下級對應Future.onCompleteSuccessFailure上的匹配,您可以獲得更簡潔的代碼,因爲在我看來,更容易看到意圖,因爲它周圍的模板較少。

我們還檢查每一個更新的工作,這一點:

if (listOfResults.forall(_.ok)) 

其中我認爲讀得很好 - 所有的結果都必須OK!

其他小動作我也收拾一下用一行代碼替換它使用了可變變量的「計數」的邏輯,:

var count = 0 
for { 
    updateWriteResult <- s.value 
} yield count += updateWriteResult.n 

變爲:

val count = listOfResults.map(_.n).sum 

即轉換將結果列表添加到整數列表中(n,UpdateWriteResult),然後使用列表中的內置sum函數完成其餘操作。

+0

謝謝你的廣泛答案!你解釋它的方式,解決方案更有意義:) 我正在努力讓它工作,我已經添加updateCity函數,至少編譯,現在我有一個問題與json被解析,但我確信,一旦解決了這個問題,再次感謝。 編輯:JSON的問題是我使用錯誤的位置:),這就像一個魅力! – semvdwal