Confused about Promises or otherwise suspended functions

Hey, i got a function that requests a json file from a server, which i figured out how to do in a blocking way thanks to coroutines.
Im now in the situation where id like to have it assigned directly in a class, but thats obviously outside of any coroutine scope.
Is it not possible to directly do that?
Like:

class Test {
val test = download("/test.json")
}

From what i can tell in pure javascript i could return a Promise and then await it, but the await function in Kotlin/js uses Coroutines, which then doesnt work since im not in a scope.
Can someone please tell me if this is impossible or if im missing something obvious?
Thanks!

1 Like

What you’re going to want to do instead is have a “smart constructor” that is a suspend function that does that for you. I’d recommend something like:

class Test(val test: Foo)
suspend fun Test() = Test(download("/test.json"))

I dont quite understand how that would allow me to assign the blolcking function to a variable directly.
Ive found that Coroutines on JVM has “Future”, which seems like something that would help me here.
Is there no equivilant in JS?

You can simply just use runBlocking, or instead you can wrap Test() in a promise

It’s not entirely clear to me what you try to achieve.

Coroutines don’t provide ways to write blocking code. It’s kind of opposite - coroutines allow to write the code that doesn’t block and they generally don’t like blocking.

Regarding object constructors, the simple answer is constructors support blocking, but don’t support suspending/coroutines. This is a technical limitation. Constructors are very special methods in many runtimes including JVM, so they can’t easily support running coroutines.

You can solve this by making download() a regular, non-coroutine/non-suspend blocking function. Also, you can “convert” a suspend function to a blocking function by using runBlocking { download(...) }. But generally, I advise against doing I/O operations inside a constructor, no matter with coroutines or without them. Constructor should be a relatively simple method with quick initialization, it shouldn’t perform too much CPU calculations nor I/O. I suggest the same as @kyay10 - use a factory function instead.

runBlocking is not available in kotlin js

Ahh, ok, I don’t have much experience with compiling Kotlin to JS, but if it doesn’t at all allow to write blocking code (which makes sense for JS), then I guess your only option is a suspending factory function instead of calling download() inside a constructor.

Also, you asked multiple times about promises/futures. Their equivalent in Kotlin is Deferred, however, I’m not exactly sure how they help here. If you meant to hold a future/promise/deferred in val test then yes, this is possible:

val test = GlobalScope.async { download("/test.json") } // or another scope

Of course then any function that accesses the value of test (they do: test.await()) has to be a suspend function. I personally still prefer a factory function.

Thank you, if you dont mind, how would i create such a factory function?

@kyay10 provided an example in the first answer. We simply don’t run download() inside a constructor, but create an additional function for creating Test objects.