The select feature of Kotlin coroutines and its application

Combination of select and Deferred

Deferred is an object representing the result of an asynchronous calculation. It can obtain the result through the await method, or judge whether it is completed through the isCompleted attribute. If there are multiple Deferred objects, we may want to get the fastest result, or display cached data first, and then display network data. At this time, we can use select to implement such logic.

select is a suspending function that receives a lambda expression as a parameter. In the lambda expression, we can use the onAwait method to obtain the result of the Deferred object and return a value. select will wait for all onAwait callbacks and choose the fastest return value as the result. For example:

select+Deferred

// 模拟从缓存中获取物品信息
suspend fun getCacheInfo(productId: String): Product {
    
    
    delay(100L)
    return Product(productId, 9.9, true)
}

// 模拟从网络中获取物品信息
suspend fun getNetworkInfo(productId: String): Product {
    
    
    delay(200L)
    return Product(productId, 9.8, false)
}

// 模拟更新UI
fun updateUI(product: Product) {
    
    
    println("${product.productId} == ${product.price} == ${product.isCache}")
}

// 数据类,来表示一个商品
data class Product(
    val productId: String,
    val price: Double,
    val isCache: Boolean = false
)

fun main() = runBlocking {
    
    
    val startTime = System.currentTimeMillis()
    val productId = "11211"
    // 开始两个非阻塞任务
    val cacheDeferred = async {
    
     getCacheInfo(productId) }
    val networkDeferred = async {
    
     getNetworkInfo(productId) }
    // 使用 select 来获取最快的结果
    val product = select<Product> {
    
    
        cacheDeferred.onAwait {
    
     it }
        networkDeferred.onAwait {
    
     it }
    }
    // 更新UI
    updateUI(product)
    println("total time : ${System.currentTimeMillis() - startTime}")
    // 如果当前是缓存信息,则再去获取网络信息
    if (product.isCache) {
    
    
        val latest = networkDeferred.await()
        updateUI(latest)
        println("all total time : ${System.currentTimeMillis() - startTime}")
    }
}

In the above code, we simulated the scenario of obtaining product information from the cache and the network. Since the cache is faster, we use select to display the cache information first, and then display the network information. The result of the operation is as follows:

11211 == 9.9 == true
total time : 134
11211 == 9.8 == false
all total time : 235

As you can see, select can help us realize the display logic of optimizing cache and network data.

The combination of select and Channel

Channel is a data structure used for communication between coroutines, which can send and receive data, and supports closure and iteration. If there are multiple Channels, we may want to get the Channel that sent data first, or execute different logic according to different Channels. At this time, we can also use select to implement such logic.

select can also receive a Channel object as a parameter. In a lambda expression, we can use the onReceiveCatching method to obtain the data sent by the Channel object and return a value. select will wait for all onReceiveCatching callbacks, and select the return value that sends data first as the result. For example:

select +Channel
fun main() = runBlocking {
    
    
    val startTime = System.currentTimeMillis()
    // 开启一个协程,往channel1中发送数据,这里发送完 ABC 需要 450ms
    val channel1 = produce {
    
    
        delay(50L)
        send("A")
        delay(150)
        send("B")
        delay(250)
        send("C")
        // 延迟 1000ms 是为了这个 Channel 不那么快 close
        // 因为 produce 高阶函数开启协程,当执行完时,会自动 close
        delay(1000)
    }
    // 开启一个协程,往channel2中发送数据,发送完 abc 需要 500ms
    val channel2 = produce {
    
    
        delay(100L)
        send("a")
        delay(200L)
        send("b")
        delay(200L)
        send("c")
        delay(1000)
    }
    // 选择 Channel ,接收两个 Channel
    suspend fun selectChannel(
        channel1: ReceiveChannel<String>,
        channel2: ReceiveChannel<String>
    ): String = select {
    
    
        // 这里同样使用类 onXXX 的 API
        channel1.onReceiveCatching {
    
     it.getOrNull() ?: "channel1 is closed!"¹[1] }
        channel2.onReceiveCatching {
    
     it.getOrNull() ?: "channel2 is closed!"¹[1] }
    }
    // 连续选择 6 次
    repeat(6) {
    
    
        val result = selectChannel(channel1, channel2)
        println(result)
    }
    // 最后再把协程取消,因为前面设置的有 1000ms 延迟
    channel1.cancel()
    channel2.cancel()
    println("Time cost: ${System.currentTimeMillis() - startTime}")
}

In the above code, we simulate the scenario where two Channels send data at the same time. Since Channel1 is faster, we use select to receive Channel1's data first. The result of the operation is as follows:

A
a
B
b
C
c
Time cost: 553

As you can see, select can help us realize the communication selection between coroutines.

Precautions and principles for using select

There are some precautions and principles for the use of select, which we need to understand:

  • select is a suspending function that needs to be used in a coroutine.
  • The callback method in select is not await or receive, but onAwait or onReceiveCatching.
  • select does not support Flow yet, because Flow already has a similar API.
  • The principle of select is based on the coroutine state machine and callback mechanism. It will register all callbacks, wait for one of the callbacks to trigger, then cancel the other callbacks, and return the result.

select is a very useful feature that allows us to choose among multiple asynchronous tasks or channels to implement optimization or selection logic.

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, but you must be able to select models, expand, and improve programming thinking. In addition, a good career plan is also very important, and the habit of learning is very important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by the senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically, so that you can systematically and efficiently Master the various knowledge points of Android development.
img
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Welcome everyone to support with one click and three links. If you need the information in the article, you can directly scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

PS: There is also a ChatGPT robot in the group, which can answer your work or technical questions
picture

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/132571013