Is there a way to wait for multiple jobs to comple...
# coroutines
m
Is there a way to wait for multiple jobs to complete? I tried using
coroutineScope { // launch here }
but in some cases those jobs don't finish before the coroutineScope block ends.
s
If the jobs don't finish before the block ends, there's something wrong with your job hierarchy. Perhaps you've passed a custom
Job
as an argument to
launch
or
async
, which would replace the normal parent job from the context.
There is an
awaitAll
function, but it sounds like it'd be better to fix the underlying problem than use
awaitAll
as a workaround
m
How would I figure out any underlying problems? I'm only using
coroutineScope
and
launch
, no weird parameters passed to them.
s
What symptom are you seeing that tells you the job isn't finished when the
coroutineScope
block returns?
m
Relevant code: https://github.com/Martmists-GH/multiplatform-everything/blob/master/src/commonMain/kotlin/com/martmists/multiplatform/graphql/GraphQL.kt#L302-L326 This should be putting all of the requested fields in a JsonObject, but in some cases it seems to ignore fields at random, even with the exact same payload (as is the case with Subscriptions). The only way fields can be missing is if an exception occurred (assuming the coroutineScope can NEVER exit early), but then the top-level function (at line 136) returns a custom error status, which isn't what it's returning.
s
First guess, a concurrency issue. Is
buildJsonObject
safe for concurrent access? If not, it could just be that concurrent updates are racing and overwriting one another.
m
How would I avoid that? AFAIK, coroutines are supposed to prevent concurrent access weirdness because only one coroutine actually "runs" at any given time, each yielding to let others run until they next yield/return.
s
That would be the case if you were using a single-threaded dispatcher, but not if your function happens to run on, say, Dispatchers.Default or Dispatchers.IO, which both have more than one thread
You could try to control the dispatcher, but if possible, I'd avoid writing coroutines that rely on single-threaded execution. Which calls inside your
launch
block are actually suspending? It's hard to tell from the GH listing.
m
the
prop.resolver
call and
interfaceMap[...].invoke
are suspending, as well as the
wrapper
call.
👍 1
s
One way to do it would be with a channel:
Copy code
coroutineScope {
  val keyValuePairs = produce {
    for (field in fields) launch {
      val keyValuePair = TODO()
      send(keyValuePair)
    }
  }
  buildJsonObject {
    for ((key, value) in keyValuePairs) put(key, value)
  }
}
☝️ untested improv code that I didn't even check to see if it compiles
With just one coroutine adding the values to the JSON object, you'll avoid any issues with concurrent access. Assuming that's your problem, which it may or may not be.
m
That seems to be working so far 👍
🐕 1