<kotlin精讲>第12章:协程coroutines
kotlin是一个非常富有生命力的语言,
设计者们在设计kotlin语言的时候参考了众多编程语言的优点,
集他们于一身.暴露最容易阅读和理解的api,让开发者愉快的运用于工程实践中.
这一章讲的协程,就是一个吸收类比了众多编程语言优点后,精心设计的一个模型.
Kotlin的协程实现了大多数主流编程语言的异步机制,
例如C#和ECMAScript(js)的async/await机制,
Go语言的composable/delimited сontinuations机制,
Python的generators/yield机制,
学习kotlin协程对理解其他编程语言的协程很有帮助.
本章主要介绍协程的用法, 讲解使用协程能带来什么好处.
探讨一下协程的大致原理. 最后介绍一下kotlin常用的协程API.
本章的主要意义是让你了解协程,
并愿意在工程实践中使用它,
优化应用程序的执行效率.
12.1同步和异步
在介绍协程之前,需要先讲明白同步和异步的概念.
举一个通俗的例子.
你打电话问书店老板 有没有黑马程序员出版的《kotlin精讲》这本书,
如果是同步代码,书店老板会说,稍等,"别挂电话,我查一下",
然后开始查找,等查好了(可能耗时5秒,也可能耗时5小时), 告诉你结果(返回结果).
如果是异步代码,你告诉书店老板, 查好了你给我打电话,然后直接挂电话了(不等待结果的产生).
当书店老板查好了,他会打电话给你.在这里老板通过“回电”这种方式就是回调.
同步和异步关注的是函数执行的结果是何时返回的
同步,就是我们在执行一个函数时,如果该函数没有执行完,我们就一直等待该函数的返回值.
换句话说, 但是一旦函数调用执行完, 我们就得到返回值了.
用上面的例子就是电话挂断的时候,我们一定就得到返回值了
而异步则是相反,我们执行一个函数时,不等待该函数是否执行完.
换句话说,当一个异步函数被执行的时候,我们可能不会立刻得到结果.
而是在函数执行的某段时间后,我们通过轮询查看执行的结果,
或通过函数的回调来告知我们结果.
用上面的例子就是,电话挂断了,我们不一定得到返回值.
需要通过每隔一段时间轮询老板来询问查询的状态.
或者等待书店老板的回调来告知查询的状态.
12.2回调函数
回调函数在java中用的也是比较多的.
我们先看一下Java代码写这种场景会有什么样的问题
- 定义回调函数的接口
public interface IService {
public void onError(String message);
default void onSuccess(){
System.out.println("找到了");
}
}
2.定义书店和找书的方法
import java.util.Random;
public class BookStore {
public void findBook(String name, IService callback){
new Thread() {
@Override
public void run() {
try {
System.out.println(("我是 " + name + " 我开始找书"));
//模拟查找书的耗时过程
Random random = new Random();
//产生一个0到9的随机数
int seed = random.nextInt(10);
//找书的时间是一个1-9秒的随机数
Thread.sleep(1000*seed);
if(seed<2){//模拟一个找不到书的大概率事件
callback.onSuccess();
}else{
throw new RuntimeException("啊,没找到书啊");
}
} catch (Exception e) {
callback.onError(e.getMessage());
}
}
}.start();
}
}
3.编写测试类
public class TestJava {
public static void main(String[] args) throws InterruptedException {
System.out.println("[顾客]:我来到书店,想找一本书");
BookStore bs = new BookStore();
bs.findBook("实习生小李", new IService() {
@Override
public void onError(String message) {
System.out.println(message);
}
});
System.out.println("[顾客]:他们去找书,我玩一会手机吧");
//...下面的代码不会阻塞,可以继续运行.
}
}
由于书店找书是一个耗时的操作,我们不清楚要耗时多久才能找到书,
所以我们传递给找书方法一个接口,我们把接口里面的onError方法实现,
这样当找不到书的时候,就会调用我们编写的onError方法,
找到书的时候会调用onSuccess方法,
这个onError方法和onSuccess方法就是我们说的回调函数
回调提供了一种避免阻塞异步执行任务的能力,
顾客可以在他们去找书的过程中,玩自己的手机.
12.3回调地狱
但是在工程实践中,由于业务可能非常复杂,
程序员会写出非常难以阅读和理解的多成嵌套的回调函数.
这种多层嵌套回调函数就叫做回调地狱(callback hell)
模拟一个场景,提供大家感受一下.
书店里有下面的一些角色,
实习生小李,老员工老王,领班老赵,仓库管理员,扫地僧,
他们的级别不同,数据库访问的权限不同.
public class TestJava {
public static void main(String[] args) {
BookStore bs = new BookStore();
bs.findBook("实习生小李", new IService() {
@Override
public void onError(String message) {
bs.findBook("老员工老王", new IService() {
@Override
public void onError(String message) {
bs.findBook("领班老赵", new IService() {
@Override
public void onError(String message) {
bs.findBook("仓库管理员", new IService() {
@Override
public void onError(String message) {
bs.findBook("扫地僧", new IService() {
@Override
public void onError(String message) {
System.out.println("真的找不到...");
}
});
}
});
}
});
}
});
}
});
}
}
12.4 协程的代码
下面是kotlin协程的代码.
我们可以利用看似同步的逻辑,写出异步的代码
大家先运行一下,感受一下效果
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import java.util.*
fun main(args: Array<String>) {
//CommanPool
launch(CommonPool) { // commonpool是协程默认实现的一个上下文,后面会详细介绍
var result = findBook("实习生小李",{println(it)}) // 异步数据,像同步代码一样return了
if(!result){
result = findBook("老员工老王",{println(it)})
}
if(!result){
result = findBook("领班老赵",{println(it)})
}
if(!result){
result = findBook("仓库管理员",{println(it)})
}
if(!result){
result = findBook("扫地僧",{println(it)})
}
}
println("我开始玩手机啦")
for(i in 1..100){
Thread.sleep(1000);
println("手机真好玩 ${i}")
}
}
suspend fun findBook(name:String,onError: (String)->Unit):Boolean {
// 请注意方法前多了一个suspend关键字,第二个参数是函数(函数式编程很爽)
return async(CommonPool) {
println("我是 $name 我开始找书")
//模拟查找书的耗时过程
val random = Random()
//产生一个0到9的随机数
val seed = random.nextInt(10)
//找书的时间是一个1-9秒的随机数
Thread.sleep((1000 * seed).toLong())
if (seed < 0) {//模拟一个找不到书的大概率事件
println("找到了")
true //lamdba表达式 true是返回值代表找到了书
} else {
onError("啊,${name} 没找到书啊");
false //没有找到书
}
}.await()
}
12.5 协程的工作方式
suspend方法工作在子线程.
声明suspend的方法,在编译的时候编译器会做特殊处理.
continuation的作用就是将当前执行流挂起, 在适合的时机再将协程恢复执行
这样异步的代码看起来好像是同步工作的一样.
点击链接加入群【kotlin入门互相监督学习群】:https://jq.qq.com/?_wv=1027&k=5rOsXwK