When sending a network request, we can display a simple loading animation on the console. The animation is printing and overwriting a set of characters in a specific order. Here we will use the character set : \ | / -
, which when printed in this order would seem like a rotating line.
For overwriting already printed character, we print the backspace character \b
. It will omit the last printed character and reset the cursor to previous position.
Iterating over the character set such that on each iteration, character is printed and then omitted for next character to be printed. This technique does not work as expected. Because computer is fast at printing on the console, some characters are overwritten so quickly that they don’t appear. This leads to inconsistency in the animation.
fun main() {
val sequence = listOf("\\", "|", "/", "-")
while (true) {
sequence.forEach {
print(it)
print('\b')
}
}
}
To make the printing process slow, we can use the delay()
function before omitting the character :
fun main() {
runBlocking {
showLoadingAnimation()
}
}
suspend fun showLoadingAnimation() {
val sequence = listOf("\\", "|", "/", "-")
while (true) {
sequence.forEach {
print(it)
delay(200)
print('\b')
}
}
}
This works as expected. Currently loading animation is shown infinitely because while
condition is set to true
. Next, we have to integrate it with a suspend function task
such that only while it executes, loading animation will be shown.
For this, let’s define a generic function executeShowingLoadingAnimation()
that takes a single parameter task
- a suspend lambda function to be executed showing loading animation.
suspend fun <T> executeShowingLoadingAnimation(
task: suspend () -> T
): T {
var output: T? = null
return coroutineScope {
launch(Dispatchers.IO) {
output = task()
}
val sequence = listOf("\\", "|", "/", "-")
while (output == null) {
sequence.forEach {
print(it)
delay(200)
print('\b')
}
}
output!!
}
}
fun main() {
runBlocking {
val response = executeShowingLoadingAnimation {
getMathFact(10)
}
println("Response = $response")
}
}
Note :
-
task
is executed on a separate thread using Dispatcher.IO
, in order to allow main thread to display loading animation parallelly. coroutineScope()
function is used to launch a child coroutine. Notice the signature of this function :
suspend fun <R> coroutineScope(
lambda: suspend CoroutineScope.() -> R
): R
It is a generic suspend function that executes the lambda whose return type is R
. After execution of lamda, it return the output of type R
. Here R
is String (output
). We’ll learn more about this function in the next section.
-
executeShowingLoadingAnimation
is a generic function where task
function generates an output of type T
and returns it.
-
while
loop condition is now changed to output == null
i.e. display while output is being generated by task function.