Given the following snippet: ```object MyObject { ...
# coroutines
m
Given the following snippet:
Copy code
object MyObject {
    var x = 1
    init {
        GlobalScope.launch {
            x++
        }
    }
}

suspend fun main() = coroutineScope {
    println(MyClass.x.toString()) // output: 1
    delay(100L)
    println(MyClass.x.toString()) // output: 2
}
How to make
MyClass.x.toString()
print 2 without needing to have a delay statement? How should I change the init block?
d
The code doesn't compile. Could you please clarify what you are trying to do? This looks quite non-idiomatic as is, so I'm wondering if there's maybe a better approach.
m
The variable ‘x’ can’t be initialized during compile-time. I get the value from a suspend function to get a resource (Compose Multiplatform Resources). There’s an if-else also as for which resource to get . But ‘x’ is used by other classes. I’m wondering about: How to use the after-init value of x without passing the coroutine scope around? Assuming I can’t modify the other classes to get the resource directly. Is there a way to make the coroutine code inside the init block blocking for example?
d
runBlocking
can call
suspend
functions from blocking code. Is that what you're looking for?
m
I’ve thought of
runBlocking
but it doesn’t seem to be available in my project
d
runBlocking
is available for JVM and Native, but not for JS or Wasm/JS. Are you targeting JS?
m
Oh that must be why yes. I’m targeting Wasm/JS
d
It's technically impossible to have non-
suspend
Wasm/JS code block while waiting for something, unfortunately. This means that you need to obtain
x
first before constructing the classes depending on it.
m
I see
I’m trying to fetch URL resource to be set as default URL in Ktor’s
defaultRequest
block, which is not a suspend block
d
I haven't worked with Compose or Ktor, so I hope someone else weighs in with practical insights here.
g
You probably want to fetch URL before you actually created Ktor instance. Or create one Ktor incstance to get default URL, another one after this
And you don't really want runBlocking for this use case even on JVM
m
How to achieve that without runBlocking though? Was thinking of doing something like this if runBlocking is available:
Copy code
init {
    runBlocking {
        val url = MainScope().async {
            return@async fetchUrlFromResource()
        }.await()
        initKtor(url)
    }
}

fun initKtor(defaultUrl: String)
suspend fun fetchUrlFromResource(): String
g
it's a really bad style to block constructor init
Copy code
suspend fun createKtorClient(): HttpClient {
        val defaultUrl = fetchUrlFromResource()
        return HttpClient(...) {
            defaultRequest {
                url(defaultUrl)

            }
        }
    }
m
I’d like to save the return value of your function to a variable. Would it be okay to create a getter that loops while the value is still null? Like this:
Copy code
private var ktor: HttpClient? = null
init {
    CoroutineScope(Dispatchers.Default).launch {
        ktor = createKtorClient()
    }
}

suspend fun getKtor(): HttpClient {
    while(ktor == null) {
        delay(100)
    }
    return ktor!!
}
d
The point is that objects shouldn't be constructed (and their
init
block shouldn't be called) until you already have an
HttpClient
.
g
I still feel that your approach is not optiomal and doesn't really endourse async programming practices
If you just want to cache it, you may use something like this
Copy code
class HttpProvider(scope: CoroutineScope) {
    private val httpClient = scope.async {
        val defaultUrl = fetchUrlFromResource()
        return HttpClient(...) {
        defaultRequest {
            url(defaultUrl)

        }
    }

    suspend fun httpClient(): HttpClient {
        return httpClient.await()
    }
}
m
Oh I've never thought of passing the scope in as dependency. Interesting 🤔
g
It's how you usually should handle scopes, without it, there is no inversion of control. So would be hard to test it I would still prefer to create an http client on app start, not delay it and also achieve inversion of control (though, even with this example above, it's possible to achieve, if you separate code above into HttpFactory and HttpProvider) But of course it depends on your architecture for this code Also one thing, now httpclient is created eagerly on class creation