What'd be the most idiomatic way to have a regular...
# coroutines
g
What'd be the most idiomatic way to have a regular, non-suspend function, launch a coroutine in the scope in which it was called?
Since it's not suspending, creating an inner scope is not an option
a
Make it an extension function on
CoroutineScope
or if you can't do that because the function already has a receiver, accept a
CoroutineScope
to launch into as a parameter
👍 1
r
If it's not a suspend function, the only option for guaranteeing that it has a coroutineContext is to make it an extension function for CoroutineScope.
1
Heh.. jinx
😄 1
g
Forgot to mention this method overrides an interface method, which I shouldn't change Do I have any alternatives, other than receiving the
CoroutineScope
on the class level? 😐
d
Well, if you want to do stupid stuff, you can use
ThreadLocalContext
, set that to the coroutine context at launch and access the thread local from your function.
👀 1
m
If you want to launch a coroutine in the context it was called in the only way to guarantee that having predictable results is to either make it a suspend function in which case you can launch the coroutine from the calling code or give the function of reference to
CoroutineScope
via a way someone else mentioned. Otherwise since the function isn’t a
suspend
function you have no guarantee it was called inside a
CoroutineScope
. Launching a coroutine in the caller’s scope is a side effect that may cause bugs when not denoted by either
suspend
or a
CoroutineScope
receiver/argument
a
it sounds like this interface might be a callback submitted to something else, if you can show us this interface or tell us a bit more about it there might be another solution to getting it into coroutines more cleanly. Using
callbackFlow
or
suspendCancellableCoroutine
and keeping an implementation of the interface completely internal to another suspending abstraction you expose is a common pattern for this sort of thing. In that setup it's much easier to communicate from non-suspending code back into suspending code.
👍 1
I think that is what we call, "snatching defeat from the jaws of victory" 😃
in attempting to reduce complexity dependencies for callers, you've added a much worse one by blocking the calling thread. Practically speaking, this means callers will need to offload it to some elastic I/O thread pool and tie up a whole thread just to wait on what is under the hood an async operation
You should simply expose this as a
suspend fun
and if a caller wants to block a thread for it, they can wrap it in their own
runBlocking
or if you really don't want to do that, just expose the
Task
API as your surface instead. Anything but blocking a thread on it.
g
I see 😅
By wrapping it in
runBlocking
, calling it from a coroutine would block the current thread, it wouldn’t be able to suspend on it, did I get this right? That’s a good point. I still need to find a way to keep this out of the domain layer interface though. The domain layer shouldn’t impose coroutines, from my point of view. This is an infrastructure concern
a
It's ok! This is how we learn! 😄
runBlocking
sets up a coroutine event loop on the calling thread; it basically means you have a coroutine dispatcher with only one thread - the caller of
runBlocking
. It lets you time-share a single calling thread that way.
I'm curious why you think a domain layer shouldn't expose
suspend
APIs. That's kind of like saying that a domain layer shouldn't use
abstract class
or extension functions or any other language feature
g
I was seeing
suspend
as a concurrency feature, a coroutines-specific feature, of which the domain layer (I’m thinking of the DDD pattern separating strict domain modeling from any infrastructure concerns). But it’s really a language feature, and it doesn’t actually impose anything on implementations, does it? 🤦🏻
It’s like suffixing the function with Async, only when you DO use coroutines the name does something for you
a
suspend fun
itself, yes. You could make an argument about the
kotlinx.coroutines
runtime library, but it's quite a small dependency and it's multiplatform; it's probably more practical to think of it as an extension to the kotlin stdlib. You wouldn't necessarily restrict using it in a domain layer any more than you'd restrict the use of
List<T>
as a utility class
we've been a bit squeamish about it on android as a dependency of high-traffic libraries for things like .apk size before tools like r8 or proguard, but if you're already using it as an implementation dependency that cost has already been paid
g
Thanks a lot, you really changed the way I look at
suspend
! This is the simplest and most abstract part of coroutines. I actually had no problem other than perspective 😅
🙌 1
It was great talking to you. Have a good night!
👋 1