Reputation: 302
I have a list of employees and I want to hit the API for each of them. In synchronous mode it takes a lot of time and I want to improve the performance with the coroutines. This is what I've done so far:
fun perform() = runBlocking {
employeesSource.getEmployees()
.map { launch { populateWithLeaveBalanceReports(it) } }
.joinAll()
}
suspend fun populateWithLeaveBalanceReports(employee: EmployeeModel) {
println("Start ${COUTNER}")
val receivedReports = reportsSource.getReports(employee.employeeId) // call to a very slow API
receivedReports { employee.addLeaveBalanceReport(it) }
println("Finish ${COUTNER++}")
}
When I try to run this, the code is being run synchronously and in the console I see the following output:
Start 0
Finish 0
Start 1
Finish 1
Start 2
Finish 2
which means that the calls are being done sequentially. If I replace this whole code in the populateWithLeaveBalanceReports
function with delay(1000)
, it will work asynchronously:
Start 0
Start 0
Start 0
Finish 0
Finish 1
Finish 2
What am I doing wrong? Any ideas??
Upvotes: 3
Views: 3630
Reputation: 323
These lines might be using a blocking code - the code that relies on blocking threads to wait for task completion.
val receivedReports = reportsSource.getReports(employee.employeeId)
receivedReports { employee.addLeaveBalanceReport(it) }
It is likely that you are using non asynchronous http client or jdbc driver under the hood of reportsSource.getReports
call .
If so, you should either
reportsSource.getReports
so it would not rely on any blocking code. This is the new / non-blocking / challenging wayUpvotes: 0
Reputation: 200148
Coroutines don't magically turn your blocking network API into non-blocking. Use launch(Dispatchers.IO) { ... }
to run blocking tasks in an elastic thread pool. Just note that this doesn't do much more than the plain-old executorService.submit(blockingTask)
. It's a bit more convenient because it uses a pre-constructed global thread pool.
Upvotes: 3