Jakub Walusiak
04/15/2020, 4:11 PMJob
from my scope later (e.g by marking it in some way)?class JobMarker(parent: Job?) : Job by Job(parent)
, but that doesn't register JobMarker as child and thus cannot be retrieveddiesieben07
04/15/2020, 4:17 PMlaunch
allows you to pass in your own CoroutineContext
. You should be able to put in your own CoroutineContext.Key
and then obtain it again later.Jakub Walusiak
04/15/2020, 4:19 PMdiesieben07
04/15/2020, 4:21 PMCoroutineScope
, right? If so, get it's Job
(every CoroutineScope
should have one!): scope.coroutineContext[Job]
. Then search through that Job's children using job.children
Jakub Walusiak
04/15/2020, 4:24 PMJob
from all children?diesieben07
04/15/2020, 4:25 PMCoroutineContext.Key
Job
is itself a CoroutineContext
, so it will have your key in itZach Klippenstein (he/him) [MOD]
04/15/2020, 4:26 PMlaunch
returns the Job
of the child, why can’t you just store that? It would be a lot simpler than writing all this search logic.Jakub Walusiak
04/15/2020, 4:27 PMCoroutineContext
, thanksZach Klippenstein (he/him) [MOD]
04/15/2020, 4:34 PMlaunch
API was designed the way it was, to return the child’s Job
. Using the existing API means you need to write less code (fewer potential bugs) and makes your code’s intent much more obvious than going out of your way to write a bunch of search logic just to avoid creating a variable.Jakub Walusiak
04/15/2020, 4:37 PMclass JobMarker(val id: String) : CoroutineContext.Element {
override val key = Key
companion object Key : CoroutineContext.Key<JobMarker>
}
fun main() = runBlocking {
launch(JobMarker("a")) { suspendCoroutine { } }
println(coroutineContext[Job]!!.children.first { it[JobMarker]?.id == "a" })
}
Zach Klippenstein (he/him) [MOD]
04/15/2020, 5:07 PMJob.children
returns a list of the job’s child jobs, not their complete contexts.
CoroutineContext
is basically just a strongly-typed Map<Key, Element>
, where the types of each entry’s keys and values are linked. When you pass a CoroutineContext
to launch
, you’re saying “take the current context, add all the entries from the new context to it (existing keys are “overwritten”), and use that as the context for the new coroutine”. So in your example, the child coroutine you launch has a context that looks something like this:
mapOf(
Job.Key to Job(parent = coroutineContext[Job]),
JobMarker.Key to JobMarker("a")
)
(there’s some other stuff too, like the dispatcher, but that’s not relevant here)
Jobs form a hierarchy, but that hierarchy only includes the jobs themselves, not the entire context. I think the reason for the confusion is that it looks a bit strange that a Job
“is a” CoroutineContext
. This doesn’t mean the Job
contains the whole context, it’s just how the api works, so that, for example, you can pass an instance of Job
to a function that takes a context and not need a bunch of job.asCoroutineContext
methods everywhere, and so that the +
operator can be defined for all of them. All `Job`s are `CoroutineContext`s, not all `CoroutineContext`s are `Job`s.Jakub Walusiak
04/15/2020, 5:09 PMZach Klippenstein (he/him) [MOD]
04/15/2020, 5:18 PMStandaloneCoroutine
, which is a private class in the coroutines library that both implements Job
and has a reference to the current and parent `CoroutineContext`s. This can be useful for debugging, but I would be extremely wary of trying to access this stuff programmatically via reflection or something, since it’s a private implementation detail and could change at any time without notice.Jakub Walusiak
04/15/2020, 5:25 PM