複数の WS 呼び出しを 1 つの結果に結合するときのエラーの処理

StackOverflow https://stackoverflow.com//questions/12666169

  •  11-12-2019
  •  | 
  •  

質問

scala、play、および Web サービスを一度に学習しているので、しばらくお待ちください。天気予報 Web サービスと Google のジオコードと場所の Web サービスを組み合わせた小さなアグリゲーター サービスをセットアップしました。何かは機能していますが、エラーを処理する適切な方法について少し混乱しています。(記事の最後にコードを載せておきました)

そのため、場所 API は緯度/経度を使用するため、ジオコード API を使用して郵便番号から緯度/経度を取得します。ジオコード API への呼び出しからの応答を処理すると、次のようになります。 (Option[String], Option[String]) (開催 maybeLoc ヴァル)。チェックする match ステートメント内 maybeLoc, 、結局そうなってしまったら (None, None), 、 私は返す Promise() を返す必要があるので、 Promise フラットマップ呼び出しから。

これに関して 2 つの質問があります。

1.) これらの flatMap またはマップ呼び出しのいずれか内でそれ以上の処理を実行できない場合に対処する正しい方法は何ですか?約束を返す必要がありますが、空の約束を返します。 Promise それは引き換えに行くときにタイムアウトしてしまうのは本当に悪い考えのようです。

2.) への呼び出しであると考えるのは正しいでしょうか? Promise() 空の Promise オブジェクトを作成すると、それを引き換えようとすると常にタイムアウトになりますか?scaladoc からは実際にはわかりませんでしたし、Google でもそれについて何も見つけることができませんでした。

私の質問があなたにとって意味があり、十分に明確であることを願っています。コードは次のとおりです。

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)
            )
        }
    }
}
役に立ちましたか?

解決

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

OptionT あたかもそれであるかのように flatMap を行うことができます Option, 、None を取得した場合は何も処理しない機能を獲得します。最後に残るのは Promise[Option[T]] これはとても良いことです。エラーを処理するもう 1 つの良い方法は、Either/EtherT を同じ方法で使用することです。

|@| アプリケーションビルダーです。2 つのオプションが必要で、 Option((Int, Int)) 両方の側がそうであれば Some. 。片面または両面の場合 None, を返すと、 None.

これを機能させるには scalaz が必要になることに注意してください Monad[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 グループでお気軽にサポートを求めてください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top