jeggy
01/14/2020, 6:27 PMwithContext function, it waits for that coroutine to finish before continuing. But I want to launch multiple coroutines into another context and then later on, I will call join on that coroutine when I know I'm finished. How would I achieve something like this? I wrote a bare minimum example of what I'm trying to achieve here: https://pl.kotl.in/Ns6Hwa_8uoctylFractal
01/14/2020, 6:30 PMlaunch(coroutineContext) not good enough?Casey Brooks
01/14/2020, 6:37 PMwithContext is usually used to change the Dispatcher, so the coroutines execute on a different thread. The lambda of that function is packed and tossed in a queue on the specified dispatcher, and the parent coroutine will continue executing in its original context.
Since you’re using runBlocking as the parent context, the join() will wait for the doLaunch calls to complete. But since runBlocking is single-threaded, it cannot exit the parent context and process the new jobs added to the queue from the withContext call. It’s deadlocked.Casey Brooks
01/14/2020, 6:38 PMrunBlocking, not withContextjeggy
01/14/2020, 6:43 PMCasey Brooks
01/14/2020, 6:48 PMDispatchers.Main onto <http://Dispatchers.IO|Dispatchers.IO>, you’ll be just fine, and you can safely join the child jobs to the parent in the Main context. But launching from Main onto Main will cause problems, again, because it is single-threaded, and the queue cannot complete the parent task to start executing the child-tasksjeggy
01/14/2020, 7:06 PMoctylFractal
01/14/2020, 7:27 PMwithContext before async? that doesn't really affect anything, async isn't a suspend function so withContext doesn't affect itjeggy
01/14/2020, 7:40 PMasync(this.coroutineContext) instead of withContext(this.coroutineContext){async(this.coroutineContext){block(..)}} all data get's lostoctylFractal
01/14/2020, 7:48 PMloadAllAtOnce(it).await()? you never complete any of the deferreds, so of course that suspends forever?octylFractal
01/14/2020, 7:49 PMoctylFractal
01/14/2020, 7:49 PMCasey Brooks
01/14/2020, 7:51 PMDeferredJsonMap is a map of objects, but you’re initializing it with deferred objects. That means at any given node in that tree, it’s going to synchronously wait for all objects in that node to be ready before continuing to populate the rest. But you’re awaiting a result in the middle that won’t be completed until later. Or, if you run the jobs async, the tree will be fully constructed before they get executed, and so cannot be added to the tree when they finish.
What you need to do is first construct a tree of purely deferred objects. Once all the entire deferred tree has been constructed, then go through the entire thing and join to wait for the results of all of them to be finished.
I’ve updated your example to populate the tree with deferreds first, and then complete. I think this is doing what you’re wanting. I’ve also added some logging traces which help illustrate how it’s executing https://pl.kotl.in/-BRc6p9wDoctylFractal
01/14/2020, 7:52 PMwithContext provides a CoroutineScope as this as well, so withContext { async {} } will suspend until the async block completes, rather than launching on the DeferredJsonMap scopeoctylFractal
01/14/2020, 7:54 PMwithContext changes things upCasey Brooks
01/14/2020, 8:06 PMjeggy
01/14/2020, 10:19 PMjeggy
01/14/2020, 11:51 PM"someArray" toDeferredValue listOf(25, 50, 100).asDeferredArray { num ->
delay(100)
"id" toValue JsonPrimitive(num)
}Casey Brooks
01/15/2020, 1:40 AMDeferredJsonArray class which holds its data in lists instead of maps, and set it up similar to the Map one. And then add a toDeferredArray method to the map class which will create the builder for it. Here’s an updated example (it could probably be cleaned up/abstracted a bit, but it looks like it works just fine) https://pl.kotl.in/FUE07Z7Lujeggy
01/15/2020, 9:19 AM