15 | 挂起函数:Kotlin协程的核心
朱涛
你好,我是朱涛。这节课,我们来学习协程的挂起函数。
挂起函数,是 Kotlin 协程当中最基础、最重要的知识点。如果对协程的挂起函数没有足够的认识,我们后续的学习将会困难重重。如果不理解挂起函数,我们将无法理解协程的非阻塞;如果不了解挂起函数,我们将无法掌握 Channel、Flow 等 API;如果不理解挂起函数,我们写出来的代码也会漏洞百出,就更别提优化软件架构了。
相反,如果能将挂起函数理解透彻,我们后面的学习也会更加轻松一些。所以这节课,我会从应用和原理两个角度,来带你理解挂起函数,包括如何使用挂起函数来优化异步任务,以及挂起函数的 CPS 当中的 Continuation 到底是什么。通过对这两个维度的学习,你在更轻易地掌握挂起函数应用场景的同时,对它的底层原理也会有一定认识。
那么接下来,你一定要打起精神,我们一起来攻克这个关键的知识点!
挂起函数:Kotlin 协程的优势
通过前面课程的学习,我们已经知道了:协程就像是轻量级的线程一样。用线程能实现的功能,我们借助 launch 和 async 也同样可以做到。
不过你可能会好奇,如果只是把 thread{} 替换成 launch{},那协程比起线程也没什么特殊的优势吧?仅仅只是因为“轻量”“非阻塞”,我们就应该放弃线程,拥抱协程吗?
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
Kotlin协程的核心特性是挂起函数,它通过suspend关键字实现非阻塞的异步操作,使得异步编程更加简洁和易于维护。挂起函数的本质是Callback,通过CPS转换实现挂起和恢复。文章深入浅出地解释了挂起函数和协程的关系,为读者提供了对Kotlin协程的深入了解和学习方向。挂起函数的类型变化和Continuation的概念解释了其实现原理。此外,文章还探索了挂起函数在协程中的调用关系,强调了挂起函数的特殊性。通过实例代码和动画形象地展示了挂起函数的执行流程,帮助读者深入理解其背后的细节。总之,本文全面介绍了Kotlin协程中挂起函数的重要性和实现原理,为读者提供了深入学习的指导方向。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《朱涛 · Kotlin 编程第一课》,新⼈⾸单¥59
《朱涛 · Kotlin 编程第一课》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(29)
- 最新
- 精选
- $Kotlinpublic 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>) } suspend函数的入参Continuation,看源码可以知道需要有一个协程上下文CoroutineContext信息,只有在协程作用域里才能传递。
作者回复: 很棒的答案。
2022-02-16221 - Airsaid老师您好,为什么 Kotlin 选择使用关键字来定义挂起函数而不是使用注解呢?(例如 Compose 就使用的是注解的方式)
作者回复: 首先,关键字其实是更好的方案,因为它是语法级的支持,注解不是。关键字是语法的一部分,而注解只是额外标注的信息,这在编译器分析抽象语法树的时候,两者的差异是很大的。 Compose之所以使用注解,是因为Compose没有增加关键字的权限,只能通过编译器插件来实现内部逻辑。
2022-02-2820 - 墨方被调用的挂起函数需要传入一个Continuation(当然这个传入也是幕后编译做的), 没有被suspend修饰的函数是没有Continuation参数的,所以被调用的挂起函数没有办法从普通函数中获取一个Continuation。
作者回复: 很棒的答案!
2022-02-1618 - colin挂起函数本身并不支持挂起,所以它没法在普通函数中调用,而它之所以能在挂起函数中调用,是因为挂起函数最终都是在协程中被调用,是协程提供了挂起函数运行的环境。
作者回复: 没错,具体的运行环境,就是Continuation还有上下文环境CoroutineContext。
2022-02-1626 - AKEI所以kotlin的挂起函数只是相当于让回调函数更简洁,相当于封装了线程池,并没有任何更高效的性能优化是吗?
作者回复: 它有非阻塞的特性,另外,它还更轻量,在特定场景下是会更好的。
2022-05-154 - Luckykelan老师您好,有个问题不太清楚 val user = getUserInfo() val friendList = getFriendList(user) val feedList = getFeedList(friendList) 这段代码和协程思维模型那张动图一起看,代码执行到getUserInfo()函数时,这个函数就被挂起了,不是应该继续执行val friendList = getFriendList(user)吗?为什么实际上在这里没有继续执行而是等待了getUserInfo()的返回呢?
作者回复: 这里的挂起,有两层含义: 1. 一个是当前线程不会被阻塞,可以执行其他任务。 2.挂起点剩下的代码,会留到之后再执行。
2022-04-2373 - 的的喀喀湖讲的确实不错,之前看了好多文章没看懂挂起的概念,跟着这两篇文章自己走了一遍代码终于能理解了
作者回复: 加油~
2022-02-273 - jim图文说明,写的真好。
作者回复: :)
2022-02-212 - InfoQ_0880b52232bf关于思考题,我想可以尝试逆向思考一下,假如普通函数可以调用挂起函数,那么会出现什么情况呢? 比如:我们在main方法里可以直接调用这三个挂起函数(实际不能直接调用),我们预期的结果是同步方式实现异步请求(这也是协程的特点之一),但其实按照非阻塞挂起的特点,main方法会直接打印“main end”,无法满足我们的预期: fun main() { val userInfo = getUserInfo() val friendList = getFeedList(userInfo) val feedList = getFeedList(friendList) println("main end") } suspend fun getUserInfo(): String { withContext(Dispatchers.IO) { delay(1000L) } return "BoyCoder" } suspend fun getFriendList(user: String): String { withContext(Dispatchers.IO) { delay(1000L) } return "Tom, Jack" } suspend fun getFeedList(list: String): String { withContext(Dispatchers.IO) { delay(1000L) } return "{FeedList..}" }
作者回复: 不错的角度,有点反证法的意思~
2022-04-181 - Paul Shansuspend -> Continuation ->CoroutineContext + resumeWith 协程上下文才是挂起和回调的幕后黑手,😀,也就是说所有的挂起函数调用的时候最终都要依托于某个协程。
作者回复: 可以这么理解。
2022-03-221
收起评论