Inside a coroutine I am doing a http-request with OkHttpClient. The request is done from a function that has the suspend
keyword:
suspend fun doSomethingFromHttp(someParam:String): Something {
...
val response = HttpReader.get(url)
return unmarshalSomething(response)!!
}
I assume that the function can be suspended on entry since it has the suspend
keyword, but will the coroutine also be suspended when doing the http-request? What about other kinds of blocking IO?
The outer async starts a coroutine. When it calls computation() , the inner async starts a second coroutine. Then, the call to await() suspends the execution of the outer async coroutine, until the execution of the inner async 's coroutine is over.
Answer: a. You should not use them for any foreground task.
The definition of suspend function: Suspend function is a function that could be started, paused, and resume.
We can see here that coroutines launched using runBlocking are not cancelable. Since cancellation occurs at suspension points, and runBlocking coroutines are not suspendable and do not have suspension points, the coroutine was allowed to complete its execution.
There's no automagic going on with Kotlin coroutines. If you call a blocking function like HttpReader.get()
, the coroutine won't be suspended and instead the call will block. You can easily assure yourself that a given function won't cause the coroutine to suspend: if it's not a suspend
function, it cannot possibly do it, whether or not it's called from a suspend
function.
If you want to turn an existing blocking API into non-blocking, suspendable calls, you must submit the blocking calls to a threadpool. The easiest way to achieve it is as follows:
val response = withContext(Dispatchers.IO) { HttpReader.get(url) }
withContext
is a suspend fun
that will suspend the coroutine, submit the provided block to another coroutine dispatcher (here IO
) and resume when that block is done and has come up with its result.
You can also easily instantiate your own ExecutorService
and use it as a coroutine dispatcher:
val myPool = Executors.newCachedThreadPool().asCoroutineDispatcher()
Now you can write
val response = withContext(myPool) { HttpReader.get(url) }
This PR has example code for proper OkHttp coroutines support
https://github.com/square/okhttp/pull/4129/files
It uses the thread pools of OkHttp to do the work. The key bit of code is this generic library code.
suspend fun OkHttpClient.execute(request: Request): Response {
val call = this.newCall(request)
return call.await()
}
suspend fun Call.await(): Response {
return suspendCancellableCoroutine { cont ->
cont.invokeOnCancellation {
cancel()
}
enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!cont.isCancelled) {
cont.resumeWithException(e)
}
}
override fun onResponse(call: Call, response: Response) {
if (!cont.isCancelled) {
cont.resume(response)
}
}
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With