Suggested way to update page on an interval?
Is there a recommended way to update my page on a set interval? For example if I want to fetch a result from my database and render it to the page every 5 seconds (maybe by updating a KVar). I can start a new coroutine that runs forever and runs delay(5000) but I don't see a way to end the coroutine when the client has left the page.
Yes, could could set up a thread to poll your database periodically using launch {} and delay. When it retrieves the data it can put them straight into Kvars which should allow the page to update in realtime without a refresh.
I think that part can work - but suppose the user is requesting a profile page users/123, and I want to keep updating that 123 user as long as the client has the page open. I need to terminate when that client's websocket closes otherwise I will have thousands of these refreshers constantly going (one per user id that was ever requested). I tested this out and it kept running in the background after the client leaves. So what I'm asking for is if there's a way to handle the websocket closing. I tried onCleanup and it doesn't get called in that case
Hey, while it's not a precise solution to your problem, I've added configurable duration for the client old states timeout in https://github.com/kwebio/kweb-core/pull/193, which you can set to something reasonably low (e.g 1H) and have some guarantees about your long-term memory usage
@shellderp do you have any code sample you could post to illustrate your experiment? Would it be an option to exit the coroutine when it is no longer possible to update the state inside of the client? Like this:
package kweb.demos.helloWorld
import kotlinx.coroutines.*
import kotlinx.coroutines.future.await
import kweb.*
import java.lang.Exception
import java.util.concurrent.CompletableFuture
import kotlin.random.Random
fun main(args: Array<String>) {
helloWorld()
}
fun helloWorld() {
Kweb(port = 16097) {
doc.body.new {
h1().text("Hello Worlds!")
GlobalScope.launch {
try {
var x = 0
while (true) {
x += 1
li().text("Hello World $x!")
val callbackId = Random.nextInt()
withTimeout(2000){
val waiter = CompletableFuture<Boolean>()
GlobalScope.launch(Dispatchers.IO){
browser.executeWithCallback("callbackWs($callbackId, {alive: 1});", callbackId) {
waiter.complete(true)
}
}
waiter.await()
}
delay(500)
}
}
catch (ex:Exception){
logger.info(ex.message)
}
finally {
logger.info("I'm gone")
}
}
}
}
}
~~Not working 100% - the coroutine never exits but at least the polling is stopped when the user leaves. Maybe a hook when the websocket disconnects would help but i'm not sure whats the best solution here. Maybe a centralized coroutine for fetching updates from the db?~~
Edit: I wrapped the executeWithCallback Call in a withTimeout closure to automatically abort if it is not finished within a given amount of time. However, i find this solution tricky to get right and i don't like it particularly. Nevertheless, it solves your problem for the time being.
Edit2: Please also note that i wrapped the executeWithCallback in another coroutine that is executed on a separate thread pool. In my experience it's often better to offload long running db calls to a separate thread pool. If a db call takes ~100ms, then your ktor/kweb project will not do any work in that time frame. However, it depends if offloading is a good idea. I'd not offload all db calls or short db calls as the offloading causes a certain overhead.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.