简体   繁体   中英

IO.async callback problem with cats.effect in Scala

Im trying to rewrite a httpclient through the java11 HttpClient in Scala

Here is my code:

import cats.effect._
import java.net.http._
import java.net.http.HttpResponse._
import java.net.http.HttpClient._

trait HttpClients[F[_]] {
  def send(req: HttpRequest)(implicit F: Async[F]): F[HttpResponse[_]]
}

object HttpClients {
  val client: HttpClient = HttpClient.newBuilder().followRedirects(Redirect.ALWAYS).build()
  def newClient[F[_] : Async](): HttpClients[F] = new HttpClients[F] {
    override def send(req: HttpRequest)(implicit F: Async[F]): F[HttpResponse[_]] = F.async { cb =>
      val resp = client.sendAsync(req, BodyHandlers.ofString())
      val s = resp.handle((res: HttpResponse[String], err: Throwable) => {
        if (err == null)
          cb(Right(res))
        else
          cb(Left(err))
      })
      s // TODO ?
      // Type missmatch
      // Required: F[Option[F[Unit]]]
      // Found:    Unit
    }
  }
}

the handle callback from this

I guess the error comes from here , but I don't know how to write next.

Then I make some change:

  def newClient[F[_] : Async](): HttpClients[F] = new HttpClients[F] {
    override def send(req: HttpRequest)(implicit F: Async[F]): F[HttpResponse[_]] = F.async[HttpResponse[_]] { cb =>
      val s = Sync[F](F: Async[F]).delay {
        val resp = client.sendAsync(req, BodyHandlers.ofString())
        resp.handle((res: HttpResponse[String], err: Throwable) => {
          if (err == null)
            cb(Right(res))
          else
            cb(Left(err))
        }).join()
      }
      F.delay(s.some)
    }
  }

This time, there is no error, but I don't know how to get the response's body

Thanks for your reply!

@OlegPyzhcov already provided insight in case you are using CE3 , this answer is using CE2 in case that is what you wanted.

The first version of the code was correct, here is a full running example using Ammonite with some style improvements and ensuring a new client is created for each call and evaluation of newClient

// scala 2.13.5

import $ivy.`org.typelevel::cats-effect:2.5.0`

import cats.effect.{Async, IO}
import cats.syntax.all._
import java.net.URI
import java.net.http.{HttpClient, HttpRequest, HttpResponse}

trait HttpClients[F[_]] {
  def send(req: HttpRequest): F[HttpResponse[String]]
}

object HttpClients {
  def newClient[F[_]](implicit F: Async[F]): F[HttpClients[F]] =
    F.delay {
      HttpClient
        .newBuilder
        .followRedirects(HttpClient.Redirect.ALWAYS)
        .build()
    } map { client =>
      new HttpClients[F] {
        override def send(req: HttpRequest): F[HttpResponse[String]] =
          F.async { cb =>
            client.sendAsync(req, HttpResponse.BodyHandlers.ofString).handle {
              (res: HttpResponse[String], err: Throwable) =>
                if (err == null) cb(Right(res))
                else cb(Left(err))
            }
          }
      }
    }
}

object Main {
  private val request =
    HttpRequest
      .newBuilder
      .GET
      .uri(URI.create("https://stackoverflow.com/questions/tagged/scala?tab=Newest"))
      .build()

  private val program = for {
    _ <- IO.delay(println("Hello, World!"))
    client <- HttpClients.newClient[IO]
    response <- client.send(request)
    _ <- IO.delay(println(response))
    _ <- IO.delay(println(response.body))
  } yield ()

  def run(): Unit = {
    program.unsafeRunSync()
  }
}

@main
def main(): Unit = {
  Main.run()
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM