Reputation: 4575
import io.ktor.client.request.*
...
val response: HttpResponse = client.get("https://example.com/version")
The code above uses ktor-client-js
and the get
and does asynchronous HTTP requests.
The problem: How to make it synchronous without lots of boilerplate code?
I am having a hard time finding an option. The only synchronized options seems not available for kotlin-js
.
Edit: The compiler asks client.get
inside a suspended method.
Edit 2: here is the code working.
If I try to remove the coroutine:
So I need to create coroutine and then boilerplate code to wait for the result. How to make all of this simpler?.
Upvotes: 0
Views: 662
Reputation: 16042
Everything in suspend functions is executed sequentially, as in any other function, there is no difference.
What is different, though, is that a suspend function is executed in the context of a coroutine scope that allows you to launch a new coroutine in that scope. You must do this explicitly though, by calling launch
or async
etc. So, everything is run synchronously, except you explicitly say otherwise.
In your example, you do not launch any new coroutines, so everything is run synchronously, each command waits for the completion of the previous command. Everything aready works as you want it to be, there is no need to fix anything.
To address the question after the edit: Referencing what I said above, client.get()
as a suspend function does only run asynchronous if you decide it should do so. That's an option, not a requirement. What it does require, though, is for you to call it in the context of a coroutine scope. That is among others, necessary for the internals of the function to switch the dispatcher to move the network request off the main thread. That doesn't need to concern you, though. The important thing is what the API exposes: get()
is a suspend function, so you are required to call that function either from another suspend function or from an explicit coroutine scope.
To reiterate: Everything is already synchronous. Only when you decide to wrap it in a coroutine and launch it in the background by calling async
it would become asynchronous. You do not do that, hence everything is already as you want it to be.
That can easily be verified by checking the return type of client.get()
: If it would be asynchronous, the return value would need to be a Promise or some other deferred object (or whatever it is in kotlin-js). It isn't, though, it is a HttpResponse
. Therefore it is impossible for that function to run asynchronously on itself. You can only make it asynchronous by wrapping it in an async
block. That block would then instantly return a Promise which will be fulfilled asynchronously at a later time.
That is, in fact, one benefit of using coroutines: The decision if or when a piece of code should block can be made by the caller. In your example, the network IO of Ktor should definitely be asynchronous at some point (since it wouln't make any sense to freeze the entire app just to wait for a network response). But at what point specifically, that is up to you - the user of the Ktor-API - to decide.
Just get a coroutine scope (your framework should provide you with at least an application level coroutine scope) and go from there. At some point you need to launch a new coroutine so you can call suspend functions. It's entirely up to you to decide at what point, though. You could wrap your entire app in that coroutine if you really would want it to run 100% synchronous.
What would make more sense, though, would be to put everything related to the network request (including the call to client.get()
) in that coroutine. Everything in that coroutine runs synchronous in relation to the other code in the coroutine. The coroutine itself will run asynchronously in relation to the rest of your app, as it should.
Upvotes: 0