更优雅的使用回调函数 —— Kotlin 协程
简断截说,上代码!
举例:
1 | fun login(name:String,pass:String){ |
上面的代码是我们比较常见的一种逻辑,发起登录请求,登录成功后使用 token 去连接其他服务,在连接成功后,继续执行其他逻辑。如果逻辑再复杂一些,比如连接失败后根据错误码去重新登录等,我们就会陷入“回调地狱”,使得代码的可读性大大降低。
就这一情况我们可以使用 Kotlin 协程来改造我们的代码:
1 | suspend fun login(name:String,pass:String):LoginBean = suspendCancellableCoroutine { ctn-> |
connect
函数的改造与 login
函数的改造思路相同,即:在获取到我们需要的数据的位置使用 continuation.resume
函数,在需要抛出异常的位置使用 continuation.resumeWithException
函数。
那么我们如何使用这两个改造完成的函数呢?代码示例如下:
1 | launch{ |
相比一开始的代码,此时的代码是不是看着逻辑更为清晰更为优雅。
改造普通函数变成挂起函数,最常用的两个函数是 suspendCoroutine
与 suspendCancellableCoroutine
,这两者几乎大同小异,都是接受一个lambda表达式作为参数。区别是,前者传入的Continuation<T>
,后者传入的是CancellableContinuation<T>
。后者可以执行continuation.invokeOnCancellation { }
函数,该函数会在协程被取消时执行。可用于一些如资源需要关闭、或者网络请求等场景,实现在协程被取消时,关闭资源or取消网络请求。
扩展阅读+温故知新:
上面我们提到了 suspend
关键字,用于修饰挂起函数,挂起函数会将当前协程挂起(可以理解类似线程阻塞),直至continuation.resume
函数执行后才能继续执行后续代码。
这无疑会导致运行速度降低,如果我们后面的部分代码不需要挂起函数的返回值,需要实现类似并行的效果,直到我们需要挂起函数结果时才挂起当前的协程该如何操作呢?
答案是 async
函数:
1 | launch{ |
async
函数的返回值是 Deferred,该接口下的await
函数是一个挂起函数。执行至此时会挂起协程,直至 async
函数内的挂起函数执行完毕获得返回值。