If I’m targeting JVM, Native, and JS, is it at all...
# coroutines
a
If I’m targeting JVM, Native, and JS, is it at all possible to write a non-suspending function in commonMain that gets the result from a suspending function? I want to implement an interface from a 3rd party library with a class in commonMain, and call an
expect suspend fun
to get the result from each target. But because it’s an external interface I can’t add
suspend
, and I can’t use
runBlocking {}
because it’s not available for JS…
d
There's always
GlobalScope.launch {}
, but it does not wait for the code in the block to finish.
a
sorry, what do you mean?
launch {}
returns a Job, but the function needs it to return a String
d
Didn't notice the code snippet, sorry, was answering your question directly. No, the way JS works, this is not possible in general.
a
ah yes, the first line in my question was ambiguous, I’ve updated it
is there a YouTrack issue for this? It seems like a pretty big drawback if there’s no way to bridge blocking/non-blocking in JS
d
Sure, there are a couple, but the problem is, this is an inherent limitation in JS: https://github.com/Kotlin/kotlinx.coroutines/issues/195
a
thanks, I’ll add a comment
d
Do you mean, to the issue? I'm pretty sure it won't change anything.
a
this is an inherent limitation in JS
I’m not a JS developer, but surely there’s a way that JS code deals with this problem? I’m not necessarily looking for an equivalent to
runBlocking {}
, but even if there’s a JS-specific function that can bridge blocking/non-blocking then I could change
Copy code
expect suspend fun getData(): String
to
Copy code
expect fun getData(): String
and then use
runBlocking {}
on JVM/Native, and something else on JS
Do you mean, to the issue? I’m pretty sure it won’t change anything.
I’d prefer to make a new issue, but if it can’t be done then all I can do is make a comment and stop working on my library
d
surely there’s a way that JS code deals with this problem
It does it by working with the equivalent of
suspend
functions. JS code is single-threaded, and
fun getData(): String
, since it's not a
suspend
fun, has to all happen in the same thread. While
await()
is waiting, the thread can't do anything else. On JS, since this is the only thread,
getData
has no chance to run.
On JS, this is done with callbacks, and
suspend
functions are abstractions over those.
... well, on the new version of JS, this is done with
async
functions actually, a direct equivalent of
suspend
functions, and there, you have the exact same inherent problem. https://stackoverflow.com/a/41976799 EDIT: wrong link
a
While
await()
is waiting, the thread can’t do anything else.
If I could change
expect suspend fun getData(): String
to
expect fun getData(): String
, then doesn’t that mean I wouldn’t need an
await()
?
also I don’t mind if JS is limited by the single threaded nature - just so long as I can write my code in commonMain, and bridge blocking/non-blocking however is best for each target.
d
First,
getData
is probably a recursive function. You probably want to rename
expect suspend fun getData(): String
to
expect suspend fun getDataImpl(): String
or something like that.
Second, yes, if
expect suspend fun getDataImpl(): String
becomes
expect fun getDataImpl(): String
, then you can implement your interface as just
override fun getData() = getDataImpl()
.
a
oh yeah, that’s confusing naming on my part :) How about this?
Copy code
// src/commonMain/kotlin/MyCommonClass.kt

import x.y.z.ThirdPartyInterface

class MyCommonClass : ThirdPartyInterface {
  override fun getData(): String = platformGetData()
}

expect suspend fun platformGetData(): String
d
However, if, on JS, you want to call a
suspend
function from a non-
suspend
one and obtain its result, it's not possible.
Kotlin/JS can't do anything that plain JS can not do, it's not magic. And plain JS can't call async functions from non-async ones.
a
right, that makes sense, thanks
the link you shared above helped too https://stackoverflow.com/a/41976799
The way JavaScript runs on a given thread is that it processes a queue
of jobs:
1. Pick up the next pending job
2. Synchronously execute the code for that job
3. Only when that job completes go back to Step 1 to pick up the next job
(It’s a bit more complicated than that, there are two levels to it, but that’s not relevant to this particular question.)
XHR completions and such are jobs that get scheduled in the queue.
There is no way to pause a job, run another job from the queue, and
then pick up the paused job. `async`/`await` provides dramatically
simpler syntax for handling asynchronous operations, but they don’t
change the nature of the job queue.
could Kotlin implement its own task queue that allows for pausing jobs?
d
In theory, it could, but then it wouldn't be able to call JS's async functions/promises, so it would be completely pointless.
a
okay, thanks for taking the time to explain