Roma  Pochanin
Roma Pochanin

Reputation: 302

Kotlin Coroutines run synchronously in spite of launch method call

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

Answers (2)

Vitaly Roslov
Vitaly Roslov

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

  1. rewrite the code of reportsSource.getReports so it would not rely on any blocking code. This is the new / non-blocking / challenging way
  2. use a threadPool executor to distribute executions manually instead of using coroutines. This is the old / simple way.

Upvotes: 0

Marko Topolnik
Marko Topolnik

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

Related Questions