Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use derived without Dispatchers.Main #30

Open
amir1376 opened this issue Mar 4, 2024 · 4 comments
Open

Use derived without Dispatchers.Main #30

amir1376 opened this issue Mar 4, 2024 · 4 comments

Comments

@amir1376
Copy link

amir1376 commented Mar 4, 2024

I have a simple console application that I want to use 'derived' to map some state flows
can I do that without Dispatchers.Main ?
I can't figure out why derived needs the Main Dispatcher . (why not using Dispatchers.Default ? by default)

@wkornewald
Copy link
Member

You can just pass dispatcher = dispatchers.default.

We use dispatchers.main by default because many iOS APIs must be called from the main thread and most lower-level APIs like Ktor switch to a different thread when necessary already. In most cases you don't gain anything by using dispatchers.default unless you're doing CPU or IO intensive operations directly on the main thread. In those rare cases you can pass a custom dispatcher. That's still better than your app crashing on iOS.

@amir1376
Copy link
Author

amir1376 commented Mar 4, 2024

Where can I pass dispatcher ?
I read the library and some of its functions accept a coroutineScope as their receivers
so here is my sample code (but it also crashes)

import com.ensody.reactivestate.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update

val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)

val number = MutableStateFlow(5)
val isPrime = appScope.derived {
    get(number) % 2 == 0
}

fun main() = runBlocking<Unit> {
    launch {
        while (isActive) {
            number.update { it + 1 }
            delay(1000)
        }
    }
    //we are in coroutineScope
    autoRun {
        println("${get(number)} is  ${get(isPrime)}")
    }
}

when I run this code I get this this exception

5 is  false
Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' and ensure it has the same version as 'kotlinx-coroutines-core'
	at kotlinx.coroutines.internal.MainDispatchersKt.throwMissingMainDispatcherException(MainDispatchers.kt:81)
	at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.missing(MainDispatchers.kt:112)
	at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.isDispatchNeeded(MainDispatchers.kt:96)
	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:319)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
	at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
	at com.ensody.reactivestate.SharedCollect.collect(OnDemandStateFlow.kt:98)
	at com.ensody.reactivestate.DefaultOnDemandStateFlow.collect(OnDemandStateFlow.kt:167)
	at com.ensody.reactivestate.StateFlowObservable$addObserver$1.invokeSuspend(AutoRunStateFlow.kt:26)
	at com.ensody.reactivestate.StateFlowObservable$addObserver$1.invoke(AutoRunStateFlow.kt)
	at com.ensody.reactivestate.StateFlowObservable$addObserver$1.invoke(AutoRunStateFlow.kt)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1$1.invokeSuspend(CoroutineLauncher.kt:61)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1$1.invoke(CoroutineLauncher.kt)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1$1.invoke(CoroutineLauncher.kt)
	at com.ensody.reactivestate.CoroutineLauncher$DefaultImpls.track(CoroutineLauncher.kt:83)
	at com.ensody.reactivestate.SimpleCoroutineLauncher.track(SimpleCoroutineLauncher.kt:10)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1.invokeSuspend(CoroutineLauncher.kt:60)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at MainKt.main(Main.kt:13)
	at MainKt.main(Main.kt)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@309e345f, Dispatchers.Main[missing]]
Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' and ensure it has the same version as 'kotlinx-coroutines-core'
	at kotlinx.coroutines.internal.MainDispatchersKt.throwMissingMainDispatcherException(MainDispatchers.kt:81)
	at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.missing(MainDispatchers.kt:112)
	at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.isDispatchNeeded(MainDispatchers.kt:96)
	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:319)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
	at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
	at com.ensody.reactivestate.SharedCollect.collect(OnDemandStateFlow.kt:98)
	at com.ensody.reactivestate.DefaultOnDemandStateFlow.collect(OnDemandStateFlow.kt:167)
	at com.ensody.reactivestate.StateFlowObservable$addObserver$1.invokeSuspend(AutoRunStateFlow.kt:26)
	at com.ensody.reactivestate.StateFlowObservable$addObserver$1.invoke(AutoRunStateFlow.kt)
	at com.ensody.reactivestate.StateFlowObservable$addObserver$1.invoke(AutoRunStateFlow.kt)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1$1.invokeSuspend(CoroutineLauncher.kt:61)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1$1.invoke(CoroutineLauncher.kt)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1$1.invoke(CoroutineLauncher.kt)
	at com.ensody.reactivestate.CoroutineLauncher$DefaultImpls.track(CoroutineLauncher.kt:83)
	at com.ensody.reactivestate.SimpleCoroutineLauncher.track(SimpleCoroutineLauncher.kt:10)
	at com.ensody.reactivestate.CoroutineLauncher$launch$1.invokeSuspend(CoroutineLauncher.kt:60)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at MainKt.main(Main.kt:13)
	at MainKt.main(Main.kt)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelled}@309e345f, Dispatchers.Main[missing]]

@wkornewald
Copy link
Member

There are two different kinds of derived: blocking and suspend based. Passing a dispatcher only makes sense for suspend based code. There you can use derived(initialValue, dispatcher = dispatchers.default). The CoroutineScope isn't absolutely necessary and it has nothing to do with the dispatcher.

However, you're trying to run this code on a target that doesn't have a main dispatcher at all. This lib currently requires one, though, and a fix would be needed to make it work at all.

@amir1376
Copy link
Author

amir1376 commented Mar 5, 2024

Thanks
That's right I was thinking that maybe you are using the scope's dispatcher.I didn't know that there is another overload that accept dispatcher

BTW, it will be good to support environments with no dispatchers (for example setting Dispatchers.Default as a fallback)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants