I was trying out a few things in Kotlin. The launch in the following code gives a compilation error. However, the GlobalScope.launch works and giving launch inside runBlocking also work.
fun main() {
launch{
} }
If you see the definition of the launch:
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job (source)
It is an extension function to the CoroutineScope, so it must be called on a CoroutineScope.
If you use the runBlocking, it'll give you a CoroutineScope as this variable in the block, so launch implicitly is this.launch.
In order to launch a coroutine it must have a lifecycle Job and a CoroutineDispatcher, which is contained in the CoroutineContext inside the CoroutineScope.
So if you want the coroutine, there is two common practise:
runBlocking (single-threaded start).
fun main() = runBlocking { // this: CoroutineScope
launch {} // implicit this.launch {}
}
coroutineScope factory function (starts at main thread but switches to default dispatcher when needed).
suspend fun main() = coroutineScope { // this: CoroutineScope
launch {} // implicit this.launch {}
}
Related
When I use Kotlin Coroutines in a Kotlin/JVM project I can add the suspend keyword to the main entry of the program.
import kotlinx.coroutines.*
suspend fun main() {
doWorld()
}
suspend fun doWorld() = coroutineScope {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
Run example in Kotlin Playground
However, when I use the same code in a Kotlin Native project, I get an error at runtime
e: Entry point can not be a suspend function.
I have found a YouTrack issue requesting suspend fun main() support in Kotlin Native.
Until the feature is available, what is the equivalent to suspend fun main() in Kotlin Native?
I'm using
Kotlin/Native v1.7.22
Kotlinx Coroutines 1.6.4
try this code...
import kotlinx.coroutines.*
fun main() {
runBlocking {
doWorld()
}
}
suspend fun doWorld() = coroutineScope {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
You need to call doWorld() method from anyother coroutine or have to make suspend function. The purpose is to change any thread to Main-thread.
Hi am trying to run following kotlin code:
Code Snippet 1
This is not working
with the error:
function 'delay' should be called only from a coroutine or another suspend function
package kotlinx.coroutines.guide.exampleBasic02
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch {echoWorldWrapper() }
println("Hello")
}
suspend fun echoWorldWrapper() {
echoWorld()
}
fun echoWorld() {
delay(1000L)
println("World!")
}
However following works:
Code Snippet 2:
package kotlinx.coroutines.guide.exampleBasic02
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch {echoWorldWrapper() }
println("Hello")
}
suspend fun echoWorldWrapper() {
delay(1000L)
println("World!")
}
In my production code I can only follow Code Snippet 1. Is there any workaround for approach 1 to work. I am new to Kotlin and couldn't find any other answer to this question. Thanks.
It is not possible to call a suspend function from a function that is not also a suspend function. suspend carries with it under the hood the state of a coroutine in a Continuation object. delay() cannot operate without that coroutine to work with.
The functionality of delay() is to suspend a coroutine for a period of time and then resume it. It doesn't make any sense for it to be called from a non-suspend function, because that means there is no coroutine to suspend.
The alternative for a non-suspend function is to use Thread.sleep(), which will block the thread for a period of time. Example:
//Use withContext to make it acceptable to call a blocking function in a coroutine
suspend fun echoWorldWrapper() = withContext(Dispatchers.IO) {
echoWorld()
}
// A blocking function:
fun echoWorld() {
Thread.sleep(1000L)
println("World!")
}
The following code are from the project architecture samples at https://github.com/android/architecture-samples
What is the lifetime kotlinx.coroutines.coroutineScope in Kotlin? Will this function saveTask return as soon as the given block and all its children coroutines are completed?
If I pass a ViewModel.viewModelScope to DefaultTasksRepository instead of kotlinx.coroutines.coroutineScope, what are differents ?
BTW, it seems that the Code A don't pass any object of CoroutineScope, why?
Code A
import kotlinx.coroutines.coroutineScope
...
class DefaultTasksRepository(
private val tasksRemoteDataSource: TasksDataSource,
private val tasksLocalDataSource: TasksDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {
...
override suspend fun saveTask(task: Task) {
coroutineScope {
launch { tasksRemoteDataSource.saveTask(task) }
launch { tasksLocalDataSource.saveTask(task) }
}
}
...
}
Code B
object ServiceLocator {
private val lock = Any()
private var database: ToDoDatabase? = null
...
private fun createTasksRepository(context: Context): TasksRepository {
val newRepo = DefaultTasksRepository(FakeTasksRemoteDataSource, createTaskLocalDataSource(context))
tasksRepository = newRepo
return newRepo
}
...
}
Added content
To Animesh Sahu: Thanks!
Are you sure that "A coroutineScope is a factory function that creates a CoroutineScope" , the following code is source code, it seems that the return value is not the object of CoroutineScope.
Source Code
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
A coroutineScope is a factory function that creates a CoroutineScope with the same context as it was called with but overriding the Job of that context.
CoroutineScope has lifetime until it is cancelled by calling cancel() on it or calling cancel() on CoroutineScope.coroutineContext or explicitly calling on the attached job coroutineContext[Job].cancel().
a coroutineScope is just a wrapper that creates immediate CoroutineScope that cancels itself up after executing its childrens.
PS: coroutineScope function is used for parallel decomposition of tasks with a new Job instance for control over its children
I have following code:
Timber.d("Calling coroutine from thread: ${Thread.currentThread().name}")
scope.launch {
Timber.d("Current thread: ${Thread.currentThread().name}")
runTest()
}
suspend fun runTest() {
coroutineScope {
launch(Dispatchers.Main) {
Timber.d("Running from thread: ${Thread.currentThread().name}")
}
}
}
If I run it. App crashes with no error in log.
In my log I see:
Calling coroutine from thread: main
Current thread: DefaultDispatcher-worker-2
But I don't see entry with Running from thread:
This is done in viewmodel
My scope looks like this:
val scope: ViewModelCoroutineScope = ViewModelCoroutineScope(Dispatchers.Default)
class ViewModelCoroutineScope(
context: CoroutineContext
) : CoroutineScope {
private var onViewDetachJob = Job()
override val coroutineContext: CoroutineContext = context + onViewDetachJob
fun onCleared() {
onViewDetachJob.cancel()
}
}
What am I doing wrong?
Moving to a different machine I finally got some errors about Dispatchers.MAIN not working.
In the end all I had to do was replace all raw coroutine dependencies with:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1")
How can I launch a coroutine from a suspend function and have it use the current Scope? (so that the Scope doesn't end until the launched coroutine also ends)
I'd like to write something like the following –
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
go()
}
suspend fun go() {
launch {
println("go!")
}
}
But this has a syntax error: "Unresolved Reference: launch". It seems launch must be run in one of the following ways –
GlobalScope.launch {
println("Go!")
}
Or
runBlocking {
launch {
println("Go!")
}
}
Or
withContext(Dispatchers.Default) {
launch {
println("Go!")
}
}
Or
coroutineScope {
launch {
println("Go!")
}
}
None of these alternatives does what I need. Either the code "blocks" instead of "spawning", or it spawns but the parent scope won't wait for its completion before the parent scope itself ends.
I need it to "spawn" (launch) in the current parent coroutine scope, and that parent scope should wait for the spawned coroutine to finish before it ends itself.
I expected that a simple launch inside a suspend fun would be valid and use its parent scope.
I'm using Kotlin 1.3 and cotlinx-coroutines-core:1.0.1.
You should make the function go an extension function of CoroutineScope:
fun main() = runBlocking {
go()
go()
go()
println("End")
}
fun CoroutineScope.go() = launch {
println("go!")
}
Read this article to understand why it is not a good idea to start in a suspend functions other coroutines without creating a new coroutineScope{}.
The convention is: In a suspend functions call other suspend functions and create a new CoroutineScope, if you need to start parallel coroutines. The result is, that the coroutine will only return, when all newly started coroutines have finished (structured concurrency).
On the other side, if you need to start new coroutines without knowing the scope, You create an extensions function of CoroutineScope, which itself it not suspendable. Now the caller can decide which scope should be used.
I believe I found a solution, which is with(CoroutineScope(coroutineContext). The following example illustrates this –
import kotlinx.coroutines.*
fun main() = runBlocking {
go()
go()
go()
println("End")
}
suspend fun go() {
// GlobalScope.launch { // spawns, but doesn't use parent scope
// runBlocking { // blocks
// withContext(Dispatchers.Default) { // blocks
// coroutineScope { // blocks
with(CoroutineScope(coroutineContext)) { // spawns and uses parent scope!
launch {
delay(2000L)
println("Go!")
}
}
}
However, Rene posted a much better solution above.
Say you are dealing with some RxJava Observable and it isn't the time to refactor them, you can now get a hold of a suspend function's CoroutineScope this way:
suspend fun yourExtraordinarySuspendFunction() = coroutineScope {
val innerScope = this // i.e. coroutineScope
legacyRxJavaUggh.subscribe { somePayloadFromRxJava ->
innerScope.launch {
// TODO your extraordinary work
}
}
}