探索Kotlin协程(三)以同步的方式写异步代码
为了解释清楚后面的代码,先介绍一些Kotlin基础知识。
Kotlin的函数定义
(Int)->Unit 表示这是一个函数类型,其输入参数为Int,输出参数为Unit
fun fetchRemote(onNext:(Int)->Unit){
Thread.sleep(300)
val value = 1
onNext(value)
}
表示函数fetchRemote有一个参数,这个参数名为onNext,它本身就是一个函数;
onNext函数的入参为Int,输出为Unit。
所以,fetchRemote的实际功能就是:
让onNext调用入参1,返回Unit对象。
再进一步
fun fetchLocal(id:Int,onNext:(Int)->Unit) {
Thread.sleep(300)
val value = 2
onNext(id + value)
}
函数fetchLocal的功能是:
将入参id加2,得到的结果作为参数传给onNext指向的函数,然后调用onNext,返回一个Unit对象。
完整代码:以回调函数方式实现耗时操作
fun fetch(){
fetchRemote { msg->
fetchLocal(msg) { result ->
Log.i("TestCoroutines003", "result:$result")
fun fetchRemote(onNext:(Int)->Unit){
Thread.sleep(300)
val value = 1
onNext(value)
fun fetchLocal(id:Int,onNext:(Int)->Unit) {
Thread.sleep(300)
val value = 2
onNext(id + value)
}
这段代码的功能是:
fetchRemote执行onNext(1),返回结果传给变量msg
fetchLocal将msg作为入参id,执行onNext(id+2),返回结果传给变量result。
最后调用println("result:$result"),将result打印出来。
所以最终的输出结果是:
I/TestCoroutines003: result:3
以一种更加容易理解的方式来阐述这段代码,就是fetchRemote得到结果后,调用回调函数fetchLocal;fetchLocal得到结果后,调用打印函数。
这句话与代码段
fetchRemote { msg->
fetchLocal(msg) { result ->
println("result:$result")
}
非常契合。代码几乎就是自然语言一对一的翻译。
以同步的代码实现异步的操作
好了,上面是以回调函数的方式实现功能。下面来看看用suspend怎么做。
suspend fun fetch():Int{
val msg = fetchRemote()
val result = fetchLocal(msg)
Log.i("TestCoroutines004", "result:$result")
return result
suspend fun fetchRemote() = suspendCoroutine<Int> {it ->
it.resume(1)
suspend fun fetchLocal(id:Int) = suspendCoroutine<Int> {
it.resume(id + 2)
}
在上一节《探索Kotlin协程(二)suspendCoroutine - https:// zhuanlan.zhihu.com/p/56 9412300 》中我们已经对suspendCoroutine进行过说明,它的作用就是通过其resume方法,将resume的入参通过Continuation.resumeWith,当作返回值返回。
所以fetchRemote()的返回值是1;
fetchLocal(1)的返回值是3。
因此最后的打印出来的还是:
I/TestCoroutines004: result:3
这个示例生动地说明了Kotlin协程是如何“用同步的代码实现异步的操作”。
编译后的代码
在前面一篇文章《用示例解释Kotlin协程 - https:// zhuanlan.zhihu.com/p/56 9250729 》中,我们介绍过Kotlin的suspend挂起关键字,这里我们再重点强调一下:
- 每个suspend修饰的函数,在编译时都会新增一个Continuation参数,而且其返回值类型变为Any!
- suspend修饿的函数返回的是不同的状态点,COROUTINE_SUSPENDED就是一个状态点。状态点用label表示。
所以,上面的fetch()函数,编译后的伪代码是这样的:
//加上一个Continuation,返回值变Any
fun fetch(completion: Continuation):Any{
when(label){
0 -> {
//label 为默认值0 ,即fetch函数被第一次调用运行,
//函数代码此时正常运行,还没接触到任何其他挂起函数
label = 1
//下面会运行一个挂起函数,所以状态label立即加1,也就是说label==1,
//表示代码运行到了第一个挂起函数,此处是fetchRemote()
val state = fetchRemote()
return COROUTINE_SUSPENDED
1 -> {
//label 1 ,表示在遇到第一个挂起函数fetchRemote() 之后,调用resume等方式恢复了调度
label = 2
//下面会运行下一个挂起函数,所以状态label立即加1,也就是说label==20
//表示代码运行到了第二个挂起函数,此处是fetchLocal()
val state = fetchLocal(id)
return COROUTINE_SUSPENDED
2 -> {
//label 2 ,表示在遇到第二个挂起函数fetchLocal() 之后,调用resume等方式恢复了调度