2

我正在同时学习 scala、play 和 web 服务,所以请耐心等待。我已经建立了一个小聚合服务,它结合了天气网络服务和谷歌的地理编码和地点网络服务。我有一些工作,但我对处理错误的正确方法有点困惑。(我把代码贴在文末)

所以地方 api 使用纬度/经度,所以我使用地理编码 api 从邮政编码中获取纬度/经度。在处理来自对地理编码 api 的调用的响应时,我最终得到一个(Option[String], Option[String])(保存在maybeLocval 中)。在检查的 match 语句中maybeLoc,如果它最终是(None, None),我会返回,Promise()因为我需要Promise从 flatmap 调用中返回 a 。

我对此有两个问题:

1.) 在这些 flatMap 或 map 调用之一中处理无法进行任何进一步处理的情况的正确方法是什么?它需要我回报一个承诺,但是Promise当我去赎回它时,做一个只会超时的空似乎是一个非常糟糕的主意。

2.)我是否正确地假设调用Promise()做一个空的承诺对象,在尝试赎回它时总是会超时?我真的无法从 scaladoc 中分辨出来,也无法从谷歌找到任何关于它的信息。

我希望我的问题对你有意义并且足够清楚。这是代码:

def bothAsJson(zipcode:String) = Action {
    val promiseOfLoc = Geocode.buildUrlFor(zipcode).get()
    val promiseOfWeather = Weather.buildUrlFor(zipcode, "json").get()

    val result = promiseOfLoc.flatMap { locResp => 
        val maybeLoc = Geocode.extractLocation(locResp.body.toString())
        maybeLoc match {
            case (Some(lat), Some(lng)) => {
                val promiseOfPlaces = Places.buildUrlFor(lat,lng).get()
                promiseOfPlaces.flatMap { placesResp =>
                    promiseOfWeather.map { weatherResp =>
                        (weatherResp.body.toString(), placesResp.body.toString())
                    }
                }
            }
            case _ => Promise()
        }
    }

    Async {
        result.orTimeout("Timeout!", 2000).map {response =>
            response.fold(
                result => Ok("Got:\n\nweather:\n" + result._1 + "\n\nplaces:\n" + result._2),
                timeout => InternalServerError(timeout)
            )
        }
    }
}
4

1 回答 1

3

如果你得到 (None, None) 你不应该期待超时,但我相信会返回另一条错误消息。我在下面提供了一个示例。

我认为您需要来自 scalaz 7 的 OptionT。我将其写为:

import scalaz._
import Scalaz._

def bothAsJson(zipcode:String) = Action {
    val promiseOfLoc = Geocode.buildUrlFor(zipcode).get.map { Option(_.body.toString()) }
    val promiseOfWeather = Weather.buildUrlFor(zipcode, "json").get
       .map{ lockResp => 
           val (lat,lng) = Geocode.extractLocation(locResp.body.toString())
           (lat |@| lng).tupled
       }
    def buildPlaces(lat: String, lng: String) = Places.buildUrlFor(lat,lng).get
       .map { Option(_.body.toString) }

    val result = (for {
       (lat, lng) <- OptionT(promiseOfLoc)
       places     <- OptionT(Places.buildUrlFor(lat,lng).get())
       weather    <- OptionT(promiseOfWeather)
    } yield (places, weather)).run

    Async {
        result.orTimeout("Timeout!", 2000).map {response =>
            response.fold(
                result => {
                  result.map(
                   some => Ok("Got:\n\nweather:\n" + some._1 + "\n\nplaces:\n" + some._2)
                  ).getOrElse(BadRequest("lat/lng failed probably?"))
                },
                timeout => InternalServerError(timeout)
            )
        }
    }
}

有了OptionTflatMap,我们就可以把它当成一个Option,如果我们得到一个 None 就可以不处理任何东西。最后,我们留下了一个Promise[Option[T]]非常好的。处理错误的另一种好方法是将 Either/EtherT 与相同的方法一起使用。

|@|是一个应用生成器。它需要 2 个选项,Option((Int, Int))如果双方都是,则返回一个Some。如果一侧或两侧是None,则返回 a None

请注意,要使其正常工作,您将需要一个 scalazMonad[Promise]实例

implicit val PromiseInstance = new Monad[Promise] {
  // override def map[A,B](fa: Promise[A])(f: A => B) = fa.map(f)
  def point[A](a: => A) = Promise.pure(a)
  def bind[A,B](fa: Promise[A])(f: A => Promise[B]) = fa.flatMap(f)
}

另请注意,我已经在 SO 编辑器中编写了所有这些代码,可能缺少大括号。但是所有的代码应该或多或少是正确的,我在 repl 中测试了它的一部分。

请随时在 freenode irc 上的 #scalaz 或 scalaz google 群组上寻求帮助。

于 2012-10-01T00:47:55.690 回答