What’s the best way to get the “current scope” for...
# coroutines
s
What’s the best way to get the “current scope” for a suspend function? In other words,
suspend fun foo() { async { ... } }
. I’d like to use async inside foo, and inherit from whatever scope foo was called with.
w
have
foo
be an extension function of
CoroutineScope
o
because of how structured concurrency works, you can only start async tasks with a lifetime as long as
foo
. to enforce this, kotlin only offers the
CoroutineScope
in suspend functions via `coroutineScope { }`:
Copy code
suspend fun foo() {
  coroutineScope { async { ... } }
}
This means that
foo
won't return until the async task completes. Or, you can have
foo
be an extension on
CoroutineScope
, but then it shouldn't be a
suspend
function, and should not use any suspend operators of its own (except inside of whatever coroutine you launch on the provided scope)
s
So is this discouraged,
suspend fun CoroutineScope.foo() { delay(1000) }
o
yes, because it's not clear which context the function uses
s
I’m just trying to understand your last comment, so is this discouraged:
fun CoroutineScope.foo() { delay(1000) }
o
that won't compile 🙂
s
I guess that discourages you against doing it then
😂
So what I’m trying to do, is I am implementing an interface,
interface Foo { suspend fun process() }
and inside this implementation I want to make 10 concurrent http requests. So based on your feedback, something like this:
Copy code
suspend fun process() {
      coroutineScope {
        val defs = urls.map {
          async(<http://Dispatchers.IO|Dispatchers.IO>) {

          }
        }
        defs.awaitAll()
      }
    }
w
It's pretty clear which scope it uses as an extension function. It uses whatever scope you call the function from, which is exactly what the original question asked. Using
coroutineScope{}
will create a brand new scope/sub-scope
o
Sam: yes, that's exactly the idea
s
I guess I can’t use CoroutineScope as an extension because inside that extension function I have no way of doing further suspendable calls ?
o
Matt: if you're referring to
suspend fun CoroutineScope.foo()
, I was talking about context, because
suspend
functions have
coroutineContext
, as does the scope.
Sam: yes, unless you did
suspend fun CoroutineScope...
, but that's not recommended because 1) it's got two different contexts and 2) it doesn't fit with the structured concurrency model
s
Thanks @octylFractal and @withoutclass
Prompt replies most helpful thank you
w
unless it's the "only applicable for top level methods" piece
Just trying to keep up, I've been away from coroutines for a few months
o
hmm, I guess they do have examples for it -- I was going off of IntelliJ's warnings for it, which given that they come from the Kotlin plugin and therefore the Kotlin team, I assumed they had some reasoning behind it -- I've never had a need for it personally, so I accepted it as a "bad" practice
though it may be the case that those warnings only occur on non-top-level functions
👍 1
w
I've never seen them myself but good to know
o
regardless, I think it goes against structured concurrency -- if you're making a
CoroutineScope
extension, it should return some form of `Job`/`Channel`/etc., something that can cancel all coroutines started from that function. If you're returning that, you've already opened a new scope (with
launch
,
async
,
produce
, etc.) to call suspend functions in, and don't need the
suspend
modifier for any of it. The only time I could see use for it is when you want to keep the same thread for some other suspend function you call outside of what you use the scope for, but that's extremely niche.
w
Every new coroutine/action started in the derived scope would be automatically cancelled provided the original scope wasn't a supervisor scope.
launch
,
async
, and
produce
are coroutine builders, they don't create new scopes.
543 Views