Home > other >  How to handle asynchronous API response in scala
How to handle asynchronous API response in scala

Time:02-03

I have an API that I need to query in scala. API returns a code that would be equal to 1 when results are ready.

I thought about an until loop to handle as the following:

var code= -1
while(code!=1){
  var response = parse(Http(URL).asString.body)
  code = response.get("code").get.asInstanceOf[BigInt].toInt
}
println(response)

But this code returns:

error: not found: value response

So I thought about doing the following:

var code = -1
var res = null.asInstanceOf[Map[String, Any]]
while(code!=1){
  var response = parse(Http(URL).asString.body)
  code = response.get("code").get.asInstanceOf[BigInt].toInt
  res = response
}
println(res)

And it works. But I would like to know if this is really the best scala-friendly way to do so ? How can I properly use a variable that outside of an until loop ?

CodePudding user response:

When you say API, do you mean you use a http api and you're using a http library in scala, or do you mean there's some class/api written up in scala? If you have to keep checking then you have to keep checking I suppose.

If you're using a Scala framework like Akka or Play, they'd have solutions to asyncrhonously poll or schedule jobs in the background as a part of their solutions which you can read about.

If you're writing a Scala script, then from a design perspective I would either run the script every 1 minute and instead of having the while loop I'd just quit until code = 1. Otherwise I'd essentially do what you've done.

Another library that could help a scala script might be fs2 or ZIO which can allow you to setup tasks that periodically poll.

This appears to be a very open question about designing apps which do polling. A specific answer is hard to give.

CodePudding user response:

You can just use simple recursion:

def runUntil[A](block: => A)(cond: A => Boolean): A = {
  @annotation.tailrec
  def loop(current: A): A =
    if (cond(current)) current
    else loop(current = block)

  loop(current = block)
}

// Which can be used like:
val response = runUntil {
  parse(Http(URL).asString.body)
} { res =>
  res.getCode == 1
}

println(response)

An, if your real code uses some kind of effect type like IO or Future

// Actually cats already provides this, is called iterateUntil
def runUntil[A](program: IO[A])(cond: A => Boolean): IO[A] = {
  @annotation.tailrec
  val loop: IO[A] =
    current.flatMap { a =>
      if (cond(a)) IO.pure(a)
      else loop
    }

  loop
}

// Used like:
val request = IO {
  parse(Http(URL).asString.body)
}
val response = runUtil(request) { res =>
  res.getCode == 1
}
response.flatMap(IO.println)

Note, for Future you would need to use () => Future instead to be able to re-execute the operation.

  • Related