What is the Purpose of the Main Dispatcher?

What is the Purpose of the Main Dispatcher?

Quick run on where is the Main Dispatcher in use

If you have this question, perhaps you are a long time server-side developer and are wondering what the Main Dispatcher is doing in the Kotlinx coroutines library? Well, the Main Dispatcher exists as kind of interface, but its implementation isn't really there.

The Main dispatcher is used in Kotlin when programming something that will interact with some sort of UI. For the Dispatchers.Main scope to be available, one of these needs to be available: Android main thread dispatcher, JavaFx, or Swing EDT dispatcher. The Main dispatcher has one distinct characteristic that tells it apart from the others. This dispatcher is singe-threaded, and it is not shipped with the kotlinx coroutines library.

Let’s look at one example:

class MainDispatcherLauncher {
    companion object {
        val logger: Logger = LoggerFactory.getLogger(MainDispatcherLauncher::class.java)
        @JvmStatic
        fun main(args: Array<String> = emptyArray()) = runBlocking {
           runMainCoroutinesTest()
        }
        private suspend fun runMainCoroutinesTest() {
            try {
                val job = CoroutineScope(Dispatchers.Main).launch {
                    launch {
                        delay(100)
                        logger.info("This is cat @ ${LocalDateTime.now()}")
                    }
                    launch {
                        logger.info("This is mouse @ ${LocalDateTime.now()}")
                    }
                    logger.info("This is master @ ${LocalDateTime.now()}")
                }
                job.join()
            } catch (ex: IllegalStateException) {
                logger.info("Error!", ex)
            }
        }
    }
}

In this case, both launches would kickstart two coroutines, and we would expect at least 3 log outputs in a normal situation.However, this will not work if simply running a standalone application or in a server - side setting . Since the Main dispatcher isn't available, running this code will result in:

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:77)
at kotlinx . coroutines . internal . MissingMainCoroutineDispatcher . missing (MainDispatchers.kt:108)
at kotlinx . coroutines . internal . MissingMainCoroutineDispatcher . isDispatchNeeded (MainDispatchers.kt:92)
at kotlinx . coroutines . test . internal . TestMainDispatcher . isDispatchNeeded (TestMainDispatcher.kt:31)
at kotlinx . coroutines . internal . DispatchedContinuationKt . resumeCancellableWith (DispatchedContinuation.kt:315)
at kotlinx . coroutines . intrinsics . CancellableKt . startCoroutineCancellable (Cancellable.kt:26)
at kotlinx . coroutines . intrinsics . CancellableKt . startCoroutineCancellable $default(Cancellable.kt:21)
at kotlinx . coroutines . CoroutineStart . invoke (CoroutineStart.kt:88)
at kotlinx . coroutines . AbstractCoroutine . start (AbstractCoroutine.kt:123)
at kotlinx . coroutines . BuildersKt_BuilderscommonKt . launch (Builders.common.kt:52)
at kotlinx . coroutines . BuildersKt . launch (Unknown Source)
at kotlinx . coroutines . BuildersKt_BuilderscommonKt . launch $default(Builders.common.kt:43)
at kotlinx . coroutines . BuildersKt . launch $default(Unknown Source)
at org . jesperancinha . ktd . MainScopeLauncher $Companion.runMainCoroutinesTest(MainScopeLauncher.kt:20)
at org . jesperancinha . ktd . MainScopeLauncher $Companion.access$runMainCoroutinesTest(MainScopeLauncher.kt:10)
at org . jesperancinha . ktd . MainScopeLauncher $Companion$main$1.invokeSuspend(MainScopeLauncher.kt:15)
at kotlin . coroutines . jvm . internal . BaseContinuationImpl . resumeWith (ContinuationImpl.kt:33)
at kotlinx . coroutines . DispatchedTask . run (DispatchedTask.kt:104)
at kotlinx . coroutines . EventLoopImplBase . processNextEvent (EventLoop.common.kt:277)
at kotlinx . coroutines . BlockingCoroutine . joinBlocking (Builders.kt:95)
at kotlinx . coroutines . BuildersKt__BuildersKt . runBlocking (Builders.kt:69)
at kotlinx . coroutines . BuildersKt . runBlocking (Unknown Source)
at kotlinx . coroutines . BuildersKt__BuildersKt . runBlocking $default(Builders.kt:48)
at kotlinx . coroutines . BuildersKt . runBlocking $default(Unknown Source)
at org . jesperancinha . ktd . MainScopeLauncher $Companion.main(MainScopeLauncher.kt:14)
at org . jesperancinha . ktd . MainScopeLauncher . main (MainScopeLauncher.kt)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine { Cancelling }@2 c039ac6, Dispatchers.Main]
06:44:49.864[main] INFO org.jesperancinha.ktd.MainScopeLauncher-- Error !
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:77)
at kotlinx . coroutines . internal . MissingMainCoroutineDispatcher . missing (MainDispatchers.kt:108)
at kotlinx . coroutines . internal . MissingMainCoroutineDispatcher . isDispatchNeeded (MainDispatchers.kt:92)
at kotlinx . coroutines . test . internal . TestMainDispatcher . isDispatchNeeded (TestMainDispatcher.kt:31)
at kotlinx . coroutines . internal . DispatchedContinuationKt . resumeCancellableWith (DispatchedContinuation.kt:315)
at kotlinx . coroutines . intrinsics . CancellableKt . startCoroutineCancellable (Cancellable.kt:26)
at kotlinx . coroutines . intrinsics . CancellableKt . startCoroutineCancellable $default(Cancellable.kt:21)
at kotlinx . coroutines . CoroutineStart . invoke (CoroutineStart.kt:88)
at kotlinx . coroutines . AbstractCoroutine . start (AbstractCoroutine.kt:123)
at kotlinx . coroutines . BuildersKt_BuilderscommonKt . launch (Builders.common.kt:52)
at kotlinx . coroutines . BuildersKt . launch (Unknown Source)
at kotlinx . coroutines . BuildersKt_BuilderscommonKt . launch $default(Builders.common.kt:43)
at kotlinx . coroutines . BuildersKt . launch $default(Unknown Source)
at org . jesperancinha . ktd . MainScopeLauncher $Companion.runMainCoroutinesTest(MainScopeLauncher.kt:20)
at org . jesperancinha . ktd . MainScopeLauncher $Companion.access$runMainCoroutinesTest(MainScopeLauncher.kt:10)
at org . jesperancinha . ktd . MainScopeLauncher $Companion$main$1.invokeSuspend(MainScopeLauncher.kt:15)
at kotlin . coroutines . jvm . internal . BaseContinuationImpl . resumeWith (ContinuationImpl.kt:33)
at kotlinx . coroutines . DispatchedTask . run (DispatchedTask.kt:104)
at kotlinx . coroutines . EventLoopImplBase . processNextEvent (EventLoop.common.kt:277)
at kotlinx . coroutines . BlockingCoroutine . joinBlocking (Builders.kt:95)
at kotlinx . coroutines . BuildersKt__BuildersKt . runBlocking (Builders.kt:69)
at kotlinx . coroutines . BuildersKt . runBlocking (Unknown Source)
at kotlinx . coroutines . BuildersKt__BuildersKt . runBlocking $default(Builders.kt:48)
at kotlinx . coroutines . BuildersKt . runBlocking $default(Unknown Source)
at org . jesperancinha . ktd . MainScopeLauncher $Companion.main(MainScopeLauncher.kt:14)
at org . jesperancinha . ktd . MainScopeLauncher . main (MainScopeLauncher.kt)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: null
Process finished with exit code 0

The Main dispatcher can be found in the following modules: kotlinx-coroutines-android, kotlinx-coroutines-javafx and kotlinx-coroutines-swing.

You can find this example in my GitRepo:

jeorg-kotlin-test-drives

Then go to :

jeorg-kotlin-coroutines/coroutines-crums-group-1/src/main/kotlin/org/jesperancinha/ktd/MainDispatcherLauncher.kt

Remember to check the source for more details:

Main Dispatcher @ Kotlin Lang

And if you have read this and have learned something new, you probably are ready to answer the following quiz:

And you can check your answer in the response video I created:

Thank you!

I hope you enjoyed this article as much as I did making it!
Please leave a review, comments or any feedback you want to give on any of the socials in the links bellow.
I’m very grateful if you want to help me make this article better.
I have placed all the source code of this application on GitHub.
Thank you for reading!