Domanda

Sto imparando Scala, giochi e servizi web tutti in una volta così sopporta con me. Ho creato un piccolo servizio di aggregatore che combina un servizio web del tempo e il geocode di Google e i servizi Web. Ho qualcosa che funziona, ma sono un po 'confuso sul modo corretto per gestire errori. (Ho postato il codice alla fine del post)

Quindi i luoghi API utilizza lat / Long e quindi uso l'API geocodificata per ottenere lat / lunghi dal codice postale. Durante l'elaborazione della risposta dalla chiamata all'API di geocodi, finisco con un (Option[String], Option[String]) (tenuto nella Val maybeLoc). Dentro l'istruzione della partita che controlla maybeLoc, se finisce per essere (None, None), restituisco Promise() perché ho bisogno di restituire un Promise dalla chiamata flatmap.

Ho due domande su questo:

1.) Qual è il modo giusto per gestire il caso di non essere in grado di effettuare ulteriori elaborazioni mentre all'interno di una di queste chiamate flatmap o mappa? Mi richiede di restituire una promessa, ma fare un Promise vuoto che è appena uscito quando vado a riscattarlo sembra una bella idea.

2.) Ho ragione nel presumere che la chiamata a Promise() faccia un oggetto promesso vuoto che sarà sempre di tempo quando cercherà di riscattarlo? Non potrei davvero dire dallo Scaladoc e non riuscivo a trovare nulla a riguardo da Google.

Spero che le mie domande avessero senso per te e sono abbastanza chiare. Ecco il codice:

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)
            )
        }
    }
}
.

È stato utile?

Soluzione

Se ottieni (nessuno, nessuno) non dovresti cercare di timeout, ma restituisci un altro messaggio di errore, credo. Ho fornito un esempio qui sotto.

Penso che tu abbia bisogno di opzione da da SCALAZ 7. Scriverò questo come:

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)
            )
        }
    }
}
.

Con OptionT possiamo flatmap come se fosse un Option, guadagnando la capacità di non elaborare nulla se ne prendiamo nessuno. Alla fine siamo rimasti con un Promise[Option[T]] che è molto bello per questo. Un altro modo giusto per gestire gli errori è usare / Ethert con lo stesso metodo.

|@| è un costruttore applicativo. Ci vogliono 2 opzioni e restituire un Option((Int, Int)) se entrambi i lati sono Some. Se un lato o entrambi i lati sono None, restituisce un None.

Nota Per questo lavoro, avrai bisogno di un'istanza Monad[Promise] SCALAZ

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)
}
.

Inoltre, si prega di notare che ho scritto tutto questo codice nell'editor in tal modo, potrebbero esserci bretelle mancanti. Ma tutto il codice dovrebbe essere più o meno giusto, ho testato parti di esso nel repl.

Non esitate a chiedere aiuto su #Scalaz su Freenode IRC, o sui gruppi di Google Shalaz.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top