https://kotlinlang.org logo
Title
a

Anders Sveen

11/17/2021, 10:44 AM
Probably a basic question: But we want to launch a "background" task from KTor route. We can't get KTor to return the payload before everything completes. Even if we wrap the slow part (no need for the result) and do
launch(<http://Dispatchers.IO|Dispatchers.IO>) { ... }
It does return 200 fast, but wait with the payload till after launch completes. Any pointers on how to achieve this? 🙂
a

Aleksei Tirman [JB]

11/17/2021, 10:46 AM
So do you want to wait for a background job completion before responding?
a

Anders Sveen

11/17/2021, 10:46 AM
No, want to respond without waiting 🙂
The other stuff we need to create the actual payload is already done 🙂
a

Aleksei Tirman [JB]

11/17/2021, 10:46 AM
The
launch
call should work in that case
Please share a code example
a

Anders Sveen

11/17/2021, 10:51 AM
post("company") {
    val company = companyService.create(call.receive<Company>())
    println("Before")
    launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        companyService.slowUpdateThing()
        println("Inside")
    }
    println("After")
    call.respond(HttpStatus.OK_200, company.id)
}
We get the sequence of: • Before • After • 200OK Headers • Inside • Actual response payload
a

Aleksei Tirman [JB]

11/17/2021, 11:13 AM
The server in the following code responds immediately and prints
Inside
after some time:
fun main() {
    embeddedServer(Netty, port = 3333) {
        install(ContentNegotiation) {
            json()
        }

        routing {
            post("company") {
                val company = call.receive<Company>()
                println("Before")
                launch(<http://Dispatchers.IO|Dispatchers.IO>) {
                    repeat(10) {
                        delay(500)
                    }
                    println("Inside")
                }
                println("After")
                call.respondText { company.x.toString() }
            }
        }
    }.start()
}

@Serializable
data class Company(val x: Int)
Maybe the problem is that
companyService
is used in both places
a

Anders Sveen

11/17/2021, 11:22 AM
Hjm, maybe. I'll have to dig down. Thanks, knowing that launch should work helps. 🙂
t

Trevor Stone

11/17/2021, 3:01 PM
You need to launch from a different coroutine scope
(not best practice) but you can try replace with GlobalScope.launch and you should see the results you're expecting
1
The correct thing to do would be to define a coroutine scope for all of these background tasks, probably with a SupervisorJob and then launch from that scope
that way you can define error handling and even cancel the scope if it is beneficial
j

Jacopo Franzoi

11/21/2021, 12:08 PM
Hello, not sure it’s completely related, but I was sharing a similar use case, with our proposed solution, and waiting for some feedback.. but didn’t received much so far.. but in case it fits your needs, we published a lightweight library for this. Hope this helps! https://kotlinlang.slack.com/archives/C0A974TJ9/p1635367313063000
a

Anders Sveen

11/21/2021, 12:27 PM
Thanks for the feedback guys. Turned out as you say that we had to do CoroutineScope(Dispatchers.IO).launch { .. }. Exactly why I am not sure, but it works. Some day I will understand CoRoutines. 😜