Kotlin | 协程async、await机制实现串行&并行请求
await: 是用于等待异步任务完成并获取结果的函数。只能用于等待异步任务返回数据。 函数会挂起当前协程,直到对应的异步任务完成,并返回任务的结果。使用示例示例1:使用 async 和 await 实现并发任务在这个示例中,我们定义了两个挂起函数和 ,分别模拟了两个网络请求。然后,我们使用启动了两个异步任务,并通过函数等待任务完成并获取结果。最后,打印出两个任务的结果。通过日志可以看到两个任务是并
文章目录
在 Kotlin 中,协程(Coroutines)提供了一种轻量级的并发编程方式。如果还不了解协程,可以参见之前的文章: Kotlin | 深入理解协程。本文来聊一聊协程中
async
和 await
机制来高效处理并发异步任务。
async 和 await 机制
- async:
async
函数用于启动一个协程,这一点与launch
的作用是一样的,不同的是,async
还会返回一个 Deferred 对象,它代表了一个未来会产生结果的值。异步任务可以在async
函数中通过suspend
修饰的挂起函数来执行。
//async源码
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
- await:
await
是用于等待异步任务完成并获取结果的函数。只能用于等待异步任务返回数据。await
函数会挂起当前协程,直到对应的异步任务完成,并返回任务的结果。
使用示例
示例1:使用 async 和 await 实现并发任务
import kotlinx.coroutines.*
private suspend fun fetchData1(): String {
delay(1000) //模拟网络请求耗时
return "data1"
}
private suspend fun fetchData2(): String {
delay(1500) //模拟网络请求耗时
return "data2"
}
fun main() = runBlocking {
val totalTime = measureTimeMillis {
val deferred1: Deferred<String> = async { fetchData1() }
val deferred2: Deferred<String> = async (start = CoroutineStart.DEFAULT) { fetchData2() }
//并发等待两个任务完成,并获取结果
val result1 = deferred1.await()
val result2 = deferred2.await()
println("result1:$result1,result2:$result2")
}
println("totalTime:$totalTime")
}
执行结果:
result1:data1,result2:data2
totalTime:1539
在这个示例中,我们定义了两个挂起函数 fetchData1
和 fetchData2
,分别模拟了两个网络请求。然后,我们使用 async
启动了两个异步任务,并通过 await
函数等待任务完成并获取结果。最后,打印出两个任务的结果。通过日志可以看到两个任务是并发执行的。
如果想实现两个任务串行执行呢?比如第二个任务需要等第一个任务执行完才能启动,只需要修改async函数的start = CoroutineStart.LAZY
即可,如下:
fun main() = runBlocking {
val totalTime = measureTimeMillis {
val deferred1: Deferred<String> = async (start = CoroutineStart.LAZY){ fetchData1() }
val deferred2: Deferred<String> = async (start = CoroutineStart.LAZY){ fetchData2() }
//...同上...
}
println("totalTime:$totalTime")
}
执行结果:
result1:data1,result2:data2
totalTime:2534
可以看到总时间变成了两个任务执行时间的总和,即串行执行。
注: 使用Flow实现同样的串行/并行效果
上面示例中的串行/并行效果,也可以通过flow中的操作符来实现:
- 对于接口之间的
串行
关系,可以使用操作符flatMapConcat
实现; - 对于接口之间的
并行
关系,可以使用操作符zip
实现。具体示例参见 Kotlin | Flow数据流的几种使用场景。
示例2:使用 async 和 await 实现超时任务
import kotlinx.coroutines.*
suspend fun fetchData(): String {
delay(2000) // 模拟网络请求耗时
return "Data"
}
fun main() = runBlocking {
val deferredResult = withTimeoutOrNull(1500) {
async { fetchData() }.await()
}
if (deferredResult != null) {
println("Result: $deferredResult")
} else {
println("Timeout occurred")
}
}
在这个示例中,我们使用 withTimeoutOrNull
函数设置了一个超时时间,如果任务在指定时间内没有完成,则返回 null
。然后,我们使用 async
启动了一个异步任务,并通过 await
函数等待任务完成。最后,根据任务是否在超时时间内完成来打印结果或者超时信息。
CoroutineStart延迟启动协程
通常,Deferred是在active状态下创建的,即当调用async后就会立即启动协程。然而,async
方法中有一个可选的CoroutineStart类型参数,用于表示协程的启动模式,当被设置为CoroutineStart.LAZY
时,此时创建的协程并不会立即执行,而是需要通过调用start、join或await来激活(上面示例中的两个接口的串行效果就是通过LAZY实现的)。
public enum class CoroutineStart {
DEFAULT, LAZY, ATOMIC, UNDISPATCHED;
}
CoroutineStart
是一个枚举类,下面是对每个枚举值的含义解释,并给出相应的使用示例:
- DEFAULT:使用默认的协程启动模式,即协程会立即启动,并在当前调度器上执行。
fun main() = runBlocking {
val job = GlobalScope.launch(start = CoroutineStart.DEFAULT) {
println("Coroutine is running")
}
job.join() // 等待协程执行完成
}
- LAZY:延迟启动协程,直到调用了协程的
start()
方法时才会启动协程。
fun main() = runBlocking {
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
println("Coroutine is running")
}
// 手动启动协程
job.start()
job.join() // 等待协程执行完成
}
- ATOMIC:以原子方式启动协程,即确保协程立即启动,并且不会被中断。
fun main() = runBlocking {
val job = GlobalScope.launch(start = CoroutineStart.ATOMIC) {
println("Coroutine is running")
}
job.join() // 等待协程执行完成
}
- UNDISPATCHED:在当前调度器上立即启动协程,但不会挂起当前协程,而是直接在当前调度器上执行协程的代码。
fun main() = runBlocking {
launch(start = CoroutineStart.UNDISPATCHED) {
println("Coroutine is running")
}
println("Coroutine is finished")
}
这些枚举值提供了不同的协程启动模式,可以根据具体的需求选择合适的启动模式来创建协程。针对串行、并行,可以总结一下:
并行请求: 多个任务都使用async{}或者async(start=CoroutineStart.DEFAULT)来创建协程;
串行请求: 多个任务都使用async(start=CoroutineStart.LAZY)来创建协程并通过deferred.await()等待协程执行结果。
最后再贴一下Coroutine继承关系图:
更多推荐
所有评论(0)