目录 总结 本质 何时使用 消除回调 一些例子 总结 挂起(suspend)函数是所有协程的核心。 挂起函数可以执行长时间运行的操作并等待它完成而不会阻塞主线程。 挂起函数的语法与常规
目录
- 总结
- 本质
- 何时使用
- 消除回调
- 一些例子
总结
挂起(suspend)函数是所有协程的核心。 挂起函数可以执行长时间运行的操作并等待它完成而不会阻塞主线程。
挂起函数的语法与常规函数的语法类似,不同之处在于添加了suspend关键字。 它可以接受一个参数并有一个返回类型。 但是,挂起函数只能由另一个挂起函数或在协程内调用。
suspend fun backgroundTask(param: Int): Int { // long running operation }
在背后,编译器将挂起函数转换为另一个没有挂起关键字的函数,该函数接受一个类型为 Continuation<T>
的附加参数。 例如,上面的函数将由编译器转换为:
fun backgroundTask(param: Int, callback: Continuation<Int>): Int { // long running operation }
本质
- 挂起函数只能在协程或者其他挂起函数中调用。
- 挂起的对象是协程:launch ,async 或者其他函数创建的协程,在执行到某一个 suspend 函数的时候,这个协程会被挂起,即,从正在执行它的线程上脱离。就是说,当前线程跳过这个挂起函数,继续往下运行,但另一方面,线程的代码在到达 suspend 函数的时候被掐断,接下来协程会从这个 suspend 函数开始继续往下执行,不过是在指定的线程,执行完后,返回到之前挂起它的线程;
- 简单来讲,在 Kotlin 中所谓的挂起,就是一个稍后会被自动切回来的线程调度操作;
- 挂起函数的特点是使用同步的方式完成异步任务。
withContext
的作用就是指定切换的线程,比如:suspend fun suspendingGetImage(id: String) = withContext(Dispatchers.IO)
。
何时使用
如果你的某个函数比较耗时,也就是要等的操作,那就把它写成 suspend 函数。这就是原则。
耗时操作一般分为两类:I/O 操作和 CPU 计算工作。比如文件的读写、网络交互、图片的模糊处理,都是耗时的,通通可以把它们写进 suspend 函数里。
另外这个「耗时」还有一种特殊情况,就是这件事本身做起来并不慢,但它需要等待,比如 5 秒钟之后再做这个操作。这种也是 suspend 函数的应用场景。
消除回调
假设 postItem
由三个有依赖关系的异步子任务组成: requestToken
,createPost
和 processPost
,这三个函数都是基于回调的 API:
// 三个基于回调的 API fun requestToken(block: (String) -> Unit) fun createPost( token: String, item: Item, block: (Post) -> Unit) ) fun processPost(post: Post) fun postItem(item: Item) { requestToken { token -> createPost(token, item) { post -> processPost(post) } } }
可以看到基于回调的 API 很容易造成大量缩进。如果代码中再加上一些条件、循环的逻辑,那么代码可读性会大大降低。Kotlin 的 suspend 关键字可以帮助我们消除回调,用同步的写法写异步:
suspend fun requestToken(): String suspend fun createPost(token: String, item: Item): Post suspend fun processPost(post) suspend fun postItem(item: Item) { val token =