Functions that have `CoroutineScope` as receiver should not be marked as `suspend`KT-W1067
Kotlin's suspend functions have an implicit CoroutineScope
which manages concurrency for them. Using an extra CoroutineScope
receiver is redundant, and may cause misbehavior because the wrong scope may be referenced when the function suspends or performs other low level concurrency related operations.
Additionally, the suspend
keyword is a semantic marker that indicates that the function is executing within a coroutine context already. Thus, such a function doesn't need to explicitly refer to a different scope and should instead either create a new coroutine scope (via coroutineScope
or other similar functions) or execute within the existing implicit scope.
In general, when a function is marked as suspend
, it means that it is okay for the executor that runs the function to stop the current coroutine and go do something else at certain "suspension points" during its execution, such as when the function is called, when you call another suspend function within this one, or when you await
a Deferred
value (await
itself is not a keyword, but a suspending function).
On the other hand, when a function is declared with CoroutineScope
as its receiver, it usually means that function will be used to launch new coroutines within the concerned scope. It is not a good idea to call such a method from within another coroutine because doing so could increase the potential for concurrency bugs due to unintentional dependencies established between the different coroutine scopes. Unless performing such an operation is absolutely required, consider using the coroutineScope
function to create a new coroutine and remove the receiver, or remove the suspend
keyword and ensure that this function is used only at the top of the coroutine scope hierarchy.
Bad Practice
suspend fun CoroutineScope.foo() {
launch {
delay(1.seconds)
}
}
suspend fun CoroutineScope.greet() {
delay(1000)
println("Well, hello friends :^)")
}
Recommended
Remove suspend
from the declaration, or convert the function to use coroutineScope
to achieve the same result.
fun CoroutineScope.foo() {
launch {
delay(1.seconds)
}
}
coroutineScope {
launch {
delay(1000)
println("Well, hello friends :^)")
}
}