挂起函数

挂起函数是指使用 suspend 关键字修饰的函数。

suspend fun getUserInfo(): String {withContext(Dispatchers.IO) {delay(1000L)}return "BoyCoder"
}

挂起和恢复

挂起函数与普通函数的区别在于:挂起函数可以挂起和恢复。挂起和恢复也是协程与线程相比的优势。

考虑下面一种场景:

  1. 获取用户信息
  2. 获取用户的好友
  3. 获取每位好友的动态

如果使用 Java,可能会这么写:

public class SuspendFunction {public void getAllFeeds() {getUserInfo(new Callback() {@Overridepublic void onSuccess(String user) {if (user != null) {System.out.println(user);getFriendList(user, new Callback() {@Overridepublic void onSuccess(String friendList) {if (friendList != null) {System.out.println(friendList);getFeedList(friendList, new Callback() {@Overridepublic void onSuccess(String feedList) {if (feedList != null) {System.out.println(feedList);}}});}}});}}});}private void getFeedList(String friendList, Callback callback) {new Thread(() -> {SystemClock.sleep(1000L);if (callback != null) {callback.onSuccess("feedList");}}).start();}private void getFriendList(String user, Callback callback) {new Thread(() -> {SystemClock.sleep(1000L);if (callback != null) {callback.onSuccess("friendList");}}).start();}private void getUserInfo(Callback callback) {new Thread(() -> {SystemClock.sleep(1000L);if (callback != null) {callback.onSuccess("user");}}).start();}public interface Callback {void onSuccess(String response);}
}

这种多重回调的模式被称为“回调地狱”,代码嵌套层次多,可读性差。

如果使用 Kotlin 的挂起函数改写,会变得很简单:

fun main() = runBlocking {val userInfo = getUserInfo()val friendList = getFriendList(userInfo)val feedList = getFeedList(friendList)println(feedList)
}suspend fun getUserInfo(): String {withContext(Dispatchers.IO) {delay(1000L)}return "BoyCoder"
}suspend fun getFriendList(user: String): String {withContext(Dispatchers.IO) {delay(1000L)}return "friendList"
}suspend fun getFeedList(friendList: String): String {withContext(Dispatchers.IO) {delay(1000L)}return "feedList"
}

挂起函数的特点是使用同步的方式完成异步任务。

以下面的代码为例

val userInfo = getUserInfo()

getUserInfo 使用 withContext 切换到 IO 线程,延迟 1 秒,然后返回结果。程序在调用 getUserInfo 时挂起,然后返回结果给 userInfo 时恢复。不需要像 Java 使用回调来传递结果。

等号 “=” 右边的代码执行在子线程并挂起,右边执行完毕后,等号 “=” 左边的代码恢复到主线程执行。

深入理解 suspend

挂起函数的本质就是 Callback。Kotlin 的编译器会将 suspend 函数转换为带有 Callback 的普通函数。

反编译之前的 Kotlin 代码,可以看出 getUserInfo 挂起函数转换为了带有 Continuation 参数的普通函数。

@Nullable
public static final Object getUserInfo(@NotNull Continuation var0) {...
}

Continuation 的定义如下:

public interface Continuation<in T> {/*** The context of the coroutine that corresponds to this continuation.*/public val context: CoroutineContext/*** Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the* return value of the last suspension point.*/public fun resumeWith(result: Result<T>)
}

Continuation 是一个接口,带有 resumeWith 方法用来返回结果。本质和 Callback 的作用是一样的。

这种将 suspend 函数转换为带有 Continuation 普通函数的过程叫做 CPS 转换

CPS:Continuation-Passing-Style Transformation。

CPS转换就是将程序接下来要执行的代码进行传递的一种模式,它将原本的同步挂起函数转换为 Callback 异步代码。

协程之所以是非阻塞,是因为它支持“挂起和恢复”,而挂起和恢复的能力,主要来自挂起函数。挂起函数是由 CPS 实现的,其中的 Continuation,本质上是 Callback。

协程与挂起函数

协程的主要能力来自挂起函数,但是协程不等同于挂起函数。

挂起函数只能在协程或者其他挂起函数中调用。因为只有协程和其他挂起函数能够提供 Continuation。

从 runBlocking 的参数 block 可以看出,block 也是一个挂起函数。

public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {

因为挂起函数能够调用挂起函数,协程提供了 block 挂起函数,所以协程也能调用挂起函数。

挂起和恢复是协程的一种底层能力,而这种能力的实现,依靠的是挂起函数。

小结

  1. 挂起函数使用 suspend 关键字表示。
  2. 挂起函数能够以同步的方式写异步代码。
  3. 挂起函数拥有挂起和恢复的能力。
  4. 挂起函数的本质是 Callback,也就是 Continuation,Kotlin 编译器会完成 CPS 转换。
  5. 挂起函数只能在协程或者其他挂起函数中调用。

Kotlin的协程:挂起函数相关推荐

  1. 破解 Kotlin 协程(6) - 协程挂起篇

    关键词:Kotlin 协程 协程挂起 任务挂起 suspend 非阻塞 协程的挂起最初是一个很神秘的东西,因为我们总是用线程的概念去思考,所以我们只能想到阻塞.不阻塞的挂起到底是怎么回事呢?说出来你也 ...

  2. Kotlin的协程与生命周期

    文章目录 一.前言 二.引入依赖 三.代码示例 1.基础用法 2.repeatOnLifecycle 3.flowWithLifecycle 4.lifecycle.whenCreated.lifec ...

  3. 十一、kotlin的协程(一)

    theme: Chinese-red 学习的前提 java线程需要大概知道点 协程是线程执行的任务, 协程和用户线程的区别在于, 协程背靠强大的编译器, 协程有专属于协程的调度器和一堆方便好用的函数, ...

  4. Kotlin中协程理解与实战(一)

    Kotlin中协程理解与实战(一) 什么是协程 在Android中协程用来解决什么问题 协程是: suspend -也称为挂起或暂停,用于暂停执行当前协程,并保存所有局部变量: resume -用于让 ...

  5. Kotlin高级协程

    Kotlin高级协程 一.前言 二.先从线程说起 三.协程的设计思想 四.协程特点:优雅的实现移步任务 五.协程基本使用 六.协程和线程相比有什么特点,如何优雅的实现异步任务 一.前言 在文章正式上干 ...

  6. 【对比Java学Kotlin】协程简史

    文章目录 一.概念释义 1.1 协程定义 1.2 与线程的关系 1.3 协程简史 二.种类划分 2.1 按调用栈分类 2.2 按调度方式分类 三.异步编程 3.1 多线程 3.2 回调 3.3 Pro ...

  7. Kotlin(3)-协程和操作符重载,Java程序员秋招三面蚂蚁金服

    Kotlin 文件和类不存在一对一关系 共生体 继承 修饰符 空指针问题 正文 重难点 协程 想了很久,关于协程的内容,在官网上确实有很多内容,基础知识概念,基本使用,以及 流操作,通道,异常处理,并 ...

  8. kotlin coroutines 协程教程-入门用法

    kotlin coroutines 协程教程-入门用法 Coroutine 协程,是kotlin 上的一个轻量级的线程库,对比 java 的 Executor,主要有以下特点: 更轻量级的 api 实 ...

  9. Kotlin 之 协程

    初识协程,启动取消协程,Flow异步流,协程并发 目录 (一)初识协程 协程是什么? Android中协程解决了什么问题? 协程的挂起与恢复 挂起和阻塞 协程的调度器 Dispatchers 任务泄露 ...

  10. 十一、kotlin的协程 - 缓存、volatile、内存屏障和cas(四) --- 跑题篇

    本章写着写着就跑题了, 又不舍得删除, 新手看 # 协程的共享变量安全问题简单入门和## volatile 不保证原子性部分代码, 其他可以不看, 太乱, 也没用 协程的共享变量安全问题简单入门 在使 ...

最新文章

  1. OC底层原理之Runtime
  2. 网关Spring Cloud Gateway科普
  3. dba_segments和dba_tables的不同
  4. python 文件修改记录_python基础-文件增删改查
  5. Javascript第六章JavaScript用new创建对象第一课
  6. PyCharm2018 汉化激活
  7. npm安装vue-cli时速度慢,fetchMetadata经常卡住并报异常
  8. html 页面加载事件,页面加载事件--DOMContentLoaded
  9. SpringBoot 通过阿里云的短信功能 实现发送手机验证码
  10. css3中-moz、-ms、-webkit,-o分别代表的意思,以及微信浏览器内核分析
  11. 根据出生日期判断星座
  12. Unity3D 本地数据持久化记录存储
  13. java数组初始化的方式_java数组初始化方式
  14. 转载-工业界视频理解解决方案大汇总(人工智能前沿讲习)
  15. 返还delegation
  16. three.js 加载obj+mtl模型
  17. 优质计算机教案,信息技术优质课教案.docx
  18. cdf日上免税店_把代购删了吧!在家也可以逛免税店
  19. 正面硬刚华龙、副业却节节败退的姚忠良,能带白象重获新生吗?
  20. 【breathandlife】气势磅礴、比较好听的旋律有哪些?

热门文章

  1. cloc工具 命令行 统计代码行数
  2. 未成年人勿进 谨以献给1980~1990出生的人(一)
  3. c语言一维数组教学设计,C语言教学中一维数组教学设计.doc
  4. matlab hanning和hann,哪位大侠能告知——为什么在MATLAB2012中不能调研(汉宁)窗函...
  5. 一起搭建私有物联网云平台讲解
  6. 复旦大学python教程_复旦大学大数据学院本科生课程学习手册.PDF
  7. moment格式化时间
  8. 计算机软件企业申请商标,软件商标注册申请流程
  9. STM32开发笔记03---Bit-Banding
  10. 《Hands-On Machine Learning with Scikit-Learn TensorFlow》读书笔记(三):分类