在工作中,发现了kotlin Coroutines
包下有大量功能非常强大的API,这篇文章中,我们主要来聊一聊select
函数
1. 什么是select函数
想象一下这个场景,在程序应用中,为了实现一个业务逻辑,你可能有好几种方式来实现,但是我只需要最快实现结果的一种方式,这时候我们就可以使用select
函数了。如果还不是很清楚啥意思,我们可以看下图:
最近黄金比较贵,我们的客户端需要实时查询黄金的价格,现在网易服务器和头条服务器都同时提供查询的接口,在同一时间节点上理论上网易服务器和头条服务器返回的数据应该是一样的,我此时同时向网易和头条服务器发送请求数据,那么我此时查询只需要最快返回结果即可。形象一点,我们可以理解为数据的军备竞赛。当然,使用我们现有的知识,完成这个也算是比较容易,使用两个线程然后加上回调即可,但是我来介绍一下如何使用select函数把逻辑做得更优雅一点。
2. 如何使用
来个简单的例子,按照上面的逻辑,我们来查查黄金的价格:
定义两个模拟函数,分别模拟向网易和头条获取黄金数据:
private suspend fun requestNestGoldData() = withContext(Dispatchers.IO){
Log.d("select","start to request nest gold data")
delay(100)
Log.d("select","get the nest gold data success")
"nest_" + 600.00
}
private suspend fun requestByteDanceData() = withContext(Dispatchers.IO) {
Log.d("select","start to request byte dance gold data")
delay(300)
Log.d("select","get the nest gold byte dance success")
"bytedance_" + 600.00
}
然后我们使用select函数进行绑定并获取数据:
viewModelScope.launch {
val requestDataResult = select {
async { requestNestGoldData() }.onAwait { it }
async { requestByteDanceData() }.onAwait { it }
}
Log.d("select","get the result : $requestDataResult")
}
然后我们来看一下打印日志:
2023-09-17 15:54:23.164 19063-19188 select D start to request nest gold data
2023-09-17 15:54:23.164 19063-19189 select D start to request byte dance gold data
2023-09-17 15:54:23.266 19063-19188 select D get the nest gold data success
2023-09-17 15:54:23.449 19063-19063 select D get the result : nest_600.0
2023-09-17 15:54:23.466 19063-19188 select D get the nest gold byte dance success
通过日志我们发现,我们的requestNestGoldData()
和requestByteDanceData()
同时进行了请求,但是select
的结果中只返回了最快的结果,当然速度慢的接口依然还在执行,只是结果丢弃了。
3. 后续
如果我们使用 回调应该怎么做呢?不妨来写一写它的伪代码实现:
interface ResultListener {
fun onResult(info String)
}
private suspend fun requestNestGoldDataWithListener(listner : Resultlistener) {
runBlocking(Dispatchers.IO){
Log.d("select","start to request nest gold data")
delay(100)
Log.d("select","get the nest gold data success")
listner.onResult("nest_" + 600.00)
}
}
private suspend fun requestByteDanceGoldDataWithListener(listner : Resultlistener) {
runBlocking(Dispatchers.IO){
Log.d("select","start to request byte dance gold data")
delay(100)
Log.d("select","get the nest byte dance data success")
listner.onResult("nest_" + 600.00)
}
}
viewModelScope.launch {
var hasResult : Boolean = false
val listener : ResultListener = object : ResultListener {
override fun onResult(info String){
if(!hasResult) {
hasResult = true
//deal with the result
}
}
}
requestNestGoldDataWithListener(listener)
requestByteDanceGoldDataWithListener(listener
}
伪代码出来了,可以看出,过程还是十分容易的,此时可能还不能看出使用回调和select
的区别,但是如果我们同时存在5条链路进行数据请求时,此时我们就可以看到select的简洁和强大。