2017-07-26 38 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