https://kotlinlang.org logo
#coroutines
Title
# coroutines
c

Colton Idle

10/11/2023, 2:38 AM
I'm reading some code of an existing codebase. It has a typical App class and the app class is using a PollingService class. Polling service basically just has to delay for 5 seconds and then call some function. To my surprise, the pollingService.startPolling() is not a suspend function. Instead the startPolling function is doing
var scope = CoroutineScope(Job() + dispatcher)
and then calling
scope.launch {delay(...) doFoo() }
. Isn't the whole point with coroutines that the top level caller is the one providing the scope? I feel like it's "bad" but I don't know why. Sure I can "pass" a scope in, but I think in this case, what I really want is maybe to inject the dispatcher/coroutine context (so its testable) and the app class would have it's own scope and the app class would do appScope.launch { pollingService.startPolling() }
👍 1
f

Francesc

10/11/2023, 5:09 AM
By convention a suspend function returns only when it has completed its task, so starting a polling service does not fit that purpose. The question of wether to pass a scope in will depend on whether the service should outlive the caller (in which case it should have its own scope) or be bound to the caller lifecycle, in which case passing it in would be appropriate.
👍 1
s

Sam

10/11/2023, 5:28 AM
A suspend function is a good choice; it lets you separate the behaviour from the choice of how to execute it, making it easier to test and clearly advertising itself as an async task. A caller can still create a scope to run it in if they want. The choice of scope comes down to things like what should happen when the polling fails (throws an exception), or when the polling should stop (e.g. if the user navigates away or the app is closed). Creating a custom scope inside the function prevents the caller from having any control over those things.
f

Francesc

10/11/2023, 6:03 AM
I'm not sure the question is clear though, a polling service by definition keeps getting data at predetermined intervals, which I would not use a suspend function to start. Something that makes one call after a delay is not a polling service and, in this case, a suspend function is appropriate, but you can just have the delay at the call site as well.
d

Dmitry Khalanskiy [JB]

10/11/2023, 6:35 AM
The pattern you're describing is common in Android. The reason as I understand it is separation of responsibilities: the fact that
PollingService
uses coroutines is an implementation detail. With an interface like
fun startPolling { scope.launch { ... } }
, you could easily rewrite the class to use separate threads or executors, for example. In a sense,
PollingService
is something like an Actor from the actor concurrency model, not a collection of functions, and
startPolling
is a command for it to, well, start polling.
As to what coroutine scopes to use, it's really up to the author. Coroutine scopes form a hierarchy so that the cancellation of a larger scope cascades into cancelling smaller scopes. In the application, is there, logically, an entity that can be cancelled/destroyed/etc., after which it doesn't make sense for
PollingService
to continue doing its thing? For example, if it no longer makes sense to proceed inside
PollingService
after some
ViewModel
gets destroyed (and its
viewModelScope
gets cancelled), then yes,
PollingService
should execute its tasks as part of that scope.
c

Colton Idle

10/11/2023, 12:49 PM
Yeah, in this case the scope should be pretty much tied to the application. So as long as the app process is alive i want to do the polling. So that's why I think either passing in a scope or calling it from a scope would make sense.
d

Dmitry Khalanskiy [JB]

10/11/2023, 1:49 PM
But it's really the opposite: if you don't ever need to cancel this task, then it doesn't need the cancellation propagation mechanism, that is, the scope hierarchy. It's the one case when manually constructing a coroutine scope makes sense.
👍 1
c

Colton Idle

10/12/2023, 1:28 AM
makes sense when you put it that way. thanks Dmitry
2 Views