Skip to content
Shreyas Patil's Blog

Exploring "select" expression of Kotlin coroutines

Cover image for Exploring "select" expression of Kotlin coroutines

Hey Kotliners 😁,

Kotlin Coroutines is a very powerful library that comes along with a lot of APIs which provide out-of-box functionalities. In this article, we will look at one of the APIs i.e. select expression.


What is a select expression? 🤨

While application development, you may have several suspended functions and you want to have a single business logic that operates on the results of the suspended functions as they complete. The coroutines library provides this capability through the select function. The select function acts as a type-safe builder, where extension functions are provided that can register callbacks on the suspendable functions.

In Kotlin coroutines, the “select” expression makes it possible to await multiple suspending functions simultaneously and selects the first result that becomes available. It’s a suspending function that is suspended until one of the clauses is either selected or fails.

The select expression can be used in some of the use cases while application development. For example:

…And a lot of use cases like this.

Let us explore it more by looking at examples.


Using it ✨

As we discussed some use cases, the very simple example of using select could be like this👇🏻:

fun main() = runBlocking<Unit> {
    val winner = select<String> {
        data1().onAwait { it }
        data2().onAwait { it }
    }

    println("The winner = $winner") // prints "The winner = Hello"
}

fun data1() = GlobalScope.async {
    delay(1000)
    "Hello"
}

fun data2() = GlobalScope.async {
    delay(2000)
    "World"
}

In this example:

Let’s take another example in which you want to perform a certain operation after a user performs some interaction on UI among the multiple choices. The select expression can be used like this 👇🏻:

suspend fun awaitCloseDialog() {
    select<Unit> {
        async { dialog.closeButton.awaitClick() }.onAwait { }
        async { dialog.okButton.awaitClick() }.onAwait { }
    }
    dialog.dismiss()
}

In this example, a dialog will be get dismissed when either the “Close” button is clicked or the “OK” button is clicked.

You can also select results from the values produced by the Channel APIs like the following 👇🏻:

fun main() = runBlocking<Unit> {
    repeat(5) {
        val number = select<Int> {
            evenChannel.onReceive { it }
            oddChannel.onReceive { it }
        }
        println(number) // prints "0 1 3 2 5"
    }
}

val evenChannel = GlobalScope.produce<Int> {
    repeat(10) {
        delay(1000)
        if (it % 2 == 0) {
            send(it)
        }
    }
}

val oddChannel = GlobalScope.produce<Int> {
    repeat(10) {
        delay(500)
        if (it % 2 != 0) {
            send(it)
        }
    }
}

In this example, the evenChannel produce even numbers after 1000ms and oddChannel produces numbers after 500ms. Thus you can see that odd numbers are selected more often than even numbers.

These were some basic examples of select expression.


Note: When multiple clauses can be selected at the same time, the first one which was executed of them gets selected on the priority i.e. the select function is biased toward the first clause. Use selectUnbiased for an unbiased selection among the clauses which simply shuffles/randomizes the selection.

List of supported select methods

These are APIs from coroutines that are supported for select expression clauses:

ReceiverSuspending functionSelect clause
JobjoinonJoin
DeferredawaitonAwait
SendChannelsendonSend
ReceiveChannelreceiveonReceive
ReceiveChannelreceiveCatchingonReceiveCatching
delayonTimeout

You can visit official documentation for exploring more available APIs around the select expression.


Final thoughts 🧐

The select expression can be used in many use cases while developing applications in Kotlin. But remember that currently this API is experimental which means that the design of the corresponding declarations has open issues which may (or may not) lead to their changes in the future.

I hope you liked this article. Please share this article if you found this helpful.

Sharing is caring! 🫶🏻

Thank you! 😃


References 📚



Previous Post
ViewModel: for UI business, not UI operations 😮
Next Post
Combining StateFlows and transforming it into a StateFlow