Coroutine Delay

Coroutine Delay
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Handler().postDelayed({ doSomething() }, 3000)
Handler().postDelayed({ doSomething() }, 3000)
Handler().postDelayed({ doSomething() }, 3000)
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
doSomething()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { doSomething() }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    doSomething()
}

A common pattern is to delay execution of some code for a given amount of time. In Android and iOS there are several ways to do, such as the above snippet. How can we pull this into shared Kotlin Multiplatform code?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
myCoroutineScope.launch {
delay(3000)
uiScope.launch {
doSomething()
}
}
myCoroutineScope.launch { delay(3000) uiScope.launch { doSomething() } }
myCoroutineScope.launch {
    delay(3000)
    uiScope.launch {
        doSomething()
    }
}

Here the uiScope.launch runs the doSomething() function on the main thread. If main thread is not needed uiScope can be left out.

There is one trick here for MPP projects. On iOS this will cause a crash at runtime. On iOS you will need to use a custom Dispatcher. Chances are that you are already as a stopgap until multithreaded coroutine support lands.

Here is a Dispatcher that supports delay() on iOS. Full source can be found in the ReadingList sample app from the ReduxKotlin project:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import kotlinx.coroutines.*
import platform.darwin.*
import kotlin.coroutines.CoroutineContext
/**
* Dispatches everything on the main thread. This is needed until
* multithreaded coroutines are supported by kotlin native.
* Overrides Delay functions to support delay() on native
*/
@UseExperimental(InternalCoroutinesApi::class)
class UI : CoroutineDispatcher(), Delay {
override fun dispatch(context: CoroutineContext, block: Runnable) {
val queue = dispatch_get_main_queue()
dispatch_async(queue) {
block.run()
}
}
@InternalCoroutinesApi
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
try {
with(continuation) {
resumeUndispatched(Unit)
}
} catch (err: Throwable) {
logError("UNCAUGHT", err.message ?: "", err)
throw err
}
}
}
@InternalCoroutinesApi
override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
val handle = object : DisposableHandle {
var disposed = false
private set
override fun dispose() {
disposed = true
}
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
try {
if (!handle.disposed) {
block.run()
}
} catch (err: Throwable) {
logError("UNCAUGHT", err.message ?: "", err)
throw err
}
}
return handle
}
}
import kotlinx.coroutines.* import platform.darwin.* import kotlin.coroutines.CoroutineContext /** * Dispatches everything on the main thread. This is needed until * multithreaded coroutines are supported by kotlin native. * Overrides Delay functions to support delay() on native */ @UseExperimental(InternalCoroutinesApi::class) class UI : CoroutineDispatcher(), Delay { override fun dispatch(context: CoroutineContext, block: Runnable) { val queue = dispatch_get_main_queue() dispatch_async(queue) { block.run() } } @InternalCoroutinesApi override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) { try { with(continuation) { resumeUndispatched(Unit) } } catch (err: Throwable) { logError("UNCAUGHT", err.message ?: "", err) throw err } } } @InternalCoroutinesApi override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle { val handle = object : DisposableHandle { var disposed = false private set override fun dispose() { disposed = true } } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) { try { if (!handle.disposed) { block.run() } } catch (err: Throwable) { logError("UNCAUGHT", err.message ?: "", err) throw err } } return handle } }
import kotlinx.coroutines.*
import platform.darwin.*
import kotlin.coroutines.CoroutineContext

/**
 * Dispatches everything on the main thread.  This is needed until
 * multithreaded coroutines are supported by kotlin native.
 * Overrides Delay functions to support delay() on native
 */
@UseExperimental(InternalCoroutinesApi::class)
class UI : CoroutineDispatcher(), Delay {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        val queue = dispatch_get_main_queue()
        dispatch_async(queue) {
            block.run()
        }
    }

    @InternalCoroutinesApi
    override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
            try {
                with(continuation) {
                    resumeUndispatched(Unit)
                }
            } catch (err: Throwable) {
                logError("UNCAUGHT", err.message ?: "", err)
                throw err
            }
        }
    }

    @InternalCoroutinesApi
    override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
        val handle = object : DisposableHandle {
            var disposed = false
                private set

            override fun dispose() {
                disposed = true
            }
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
            try {
                if (!handle.disposed) {
                    block.run()
                }
            } catch (err: Throwable) {
                logError("UNCAUGHT", err.message ?: "", err)
                throw err
            }
        }

        return handle
    }

}

Software Engineer, Husband, and father of 2 amazing kids. Android, iOS, Kotlin multiplatform!! Maintainer of ReduxKotlin.org