I took this simple code snippet from kotlin examples:
fun main(args: Array<String>) = runBlocking<Unit> {
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
}
When I try to run it, it throws
java.lang.NoSuchMethodError: kotlinx.coroutines.experimental.ScheduledKt.withTimeout$default(JLjava/util/concurrent/TimeUnit;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/lang/Object;
I use kotlinVersion = '1.1.51'
Any advice?
In your case, make sure that in your build.gradle file you enable coroutines :
kotlin {
experimental {
coroutines "enable"
}
}
Also you should trigger your coroutine with launch(UI) and call explicitly wait() and the suspended methods. The rest of your code will be executed on the UI thread.
Hope it resolved your issue!
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!")
}
I want to instantly respond to the client and handle the request in the background by launching a coroutine. First i tried the following solution:
suspend fun PipelineContext<Unit, ApplicationCall>.handleTest(value: Unit) {
// insert calls to suspending functions here
launch {
repeat(10000) {
println("Executing background task $it.")
delay(1000)
}
}
call.respond("Executing the task in background")
}
routing {
get("/test", PipelineContext<Unit, ApplicationCall>::handleTest)
}
This works as expected. It returns instantly and executes the background task.
Though, IntelliJ IDE gives me the following warning:
Ambiguous coroutineContext due to CoroutineScope receiver of suspend function
I know what this warning means and why it occurs, so I tried to find a way around this:
suspend fun handleTest(context: PipelineContext<Unit, ApplicationCall>) {
// insert calls to suspending functions here
context.launch {
repeat(10000) {
println("Executing background task $it.")
delay(1000)
}
}
context.call.respond("Executing the task in background")
}
routing {
get("/test") {
handleTest(this)
}
}
This piece of code also works as expected, however it looks wrong to me when reading this article, https://elizarov.medium.com/explicit-concurrency-67a8e8fd9b25. The author explains that you should not launch coroutines inside a suspending function unless wrapping it in a new coroutineScope {}.
I was curious and tried out to inline handleTest:
routing {
get("/test") {
// insert calls to suspending functions here
launch {
repeat(10000) {
println("Executing background task $it.")
delay(1000)
}
}
call.respond("Executing the task in background")
}
}
This also works as expected, and even the warning is gone. However, the construct is theoretically still the same as in the first solution.
What is the correct solution to my problem?
You can use CoroutineScope:
routing {
get("/test") {
// insert calls to suspending functions here
CoroutineScope(Job()).launch {
repeat(10000) {
println("Executing background task $it.")
delay(1000)
}
}
call.respond("Executing the task in background")
}
It will create a new scope decouple the handler context.
I suggest creating a new coroutine scope to make it clear from where the coroutineContext is taken because both CoroutineScope and suspend function have it.
suspend fun PipelineContext<Unit, ApplicationCall>.handleTest(value: Unit) = coroutineScope {
// insert calls to suspending functions here
launch {
repeat(10000) {
println("Executing background task $it.")
delay(1000)
}
}
call.respond("Executing the task in background")
}
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
}
}
}