https://kotlinlang.org logo
#coroutines
Title
# coroutines
h

hmole

10/31/2018, 3:51 PM
Hello. I'm trying to make 3 http requests concurrently and return single result(like rx
zip
) with coroutines. Api calls are
Deferred<T>
with
retrofit-coroutines-adapter
. But they don't seem to execute in parallel. What am I doing wrong?
message has been deleted
m

mp

10/31/2018, 3:52 PM
Don't call await() immediately.
Create all three Deferreds, then await() them when you actually need the value (in your Result ctor)
h

hmole

10/31/2018, 3:53 PM
Do I need to use
awaitAll
?
m

mp

10/31/2018, 3:55 PM
Copy code
val worfklows = fsApi.workflows()
val foo = ...
...
Result(workflows.await(), foo.await(), ...)
You want to start all the requests before you block on having any of the results available.
h

hmole

10/31/2018, 3:58 PM
That's not helping sadly, here's the log of requests
Copy code
18:57:06.406 GET 200 1733ms <http://example.com/workflows>
18:57:07.916 GET 200 1479ms <http://example.com/issues/types>
18:57:09.250 GET 200 1326ms <http://example.com/issues/statuses>
m

mp

10/31/2018, 3:59 PM
Then retrofit is probably lazily starting the requests.
I seem to remember it doing that when I last used it years ago
Configure retrofit to not do that, if possible, or use a more flexible http client.
Or, syntactically messy, but might work around this issue... val foo = async { fsApi.workflows().await() }
h

hmole

10/31/2018, 4:05 PM
Thanks, they fire concurrently now. This concludes my experiment with coroutines I think, back go rxjava.🤔
j

Jonathan

10/31/2018, 4:27 PM
And don't use function returning deferred. Write suspending function instead....
m

mp

10/31/2018, 4:31 PM
he can't help that, that's just retrofit doing its thing
j

Jonathan

10/31/2018, 4:31 PM
Copy code
suspend fun FsApi.loadWorkflows(): List<Workflow> { ... }
suspend fun FsApi.loadTypes(): List<IssueType> { ... }
suspend fun FsApi.loadStatuses(): List<IssueStatus> { ... }

suspend fun loadFsData(): Result = coroutineScope {
	val workflows = async { fsApi.loadWorkflows() }
	val types = async { fsApi.loadTypes() }
	val statuses = async { fsApi.loadStatuses() }
	
	Result(workflows.await(), types.await(), statuses.await())
}
@hmole. Coroutines is not about
async
it is about
suspend
which makes asynchronous code way much simpler that futures or rxjava 😉
m

mp

10/31/2018, 4:33 PM
indeed. I've used rxjava, and you can certainly be successful with it, but suspending code is typically easier to reason about.
j

Jonathan

10/31/2018, 4:35 PM
retrofit'api provide function which returns deferred?
m

mp

10/31/2018, 4:40 PM
I'm just guessing, but I bet the existing future-based retrofit support just tacks on
.asDeferred()
j

Jonathan

10/31/2018, 4:41 PM
In this case the code is still simpler that what we'd get with RxJava:
Copy code
suspend fun loadFsData(): Result  {
	val workflows = fsApi.workflows().asDeferred()
	val types = fsApi.types().asDeferred()
	val statuses = fsApi.statuses().asDeferred()
	
	return Result(workflows.await(), types.await(), statuses.await())
}
where function return
Deferred
instead of being suspending function, which is a bit sad.
It was probably not possible to do better though
Copy code
suspend fun loadFsData(): Result  {
    val workflows = fsApi.workflows()
    val types = fsApi.types()
    val statuses = fsApi.statuses()
    
    return Result(workflows.await(), types.await(), statuses.await())
}
d

dekans

10/31/2018, 5:33 PM
z

Zach Klippenstein (he/him) [MOD]

11/01/2018, 6:25 PM
first-class
suspend
function support is a WIP: https://github.com/square/retrofit/pull/2886
4 Views