- 深入理解Kotlin协程
- 霍丙乾
- 630字
- 2022-02-25 09:35:45
1.2.2 异常处理
同步逻辑的异常处理非常直观,我们可以简单地用try...catch语句来实现对整个流程任意位置的异常的捕获,但异步逻辑的异常处理就显得不是很直接了。
首先简化asyncBitmap函数,去掉立即返回结果的路径,保留常见的回调写法,并增加对异常的处理,如代码清单1-7所示。
代码清单1-7 异步回调的异常处理
fun asyncBitmap( url: String, onSuccess: (Bitmap) -> Unit, onError: (Throwable) -> Unit ) { thread { try { download(url).also(onSuccess) } catch (e: Exception) { onError(e) } } }
调用的时候我们很自然地传入一个异常处理函数,如代码清单1-8所示。
代码清单1-8 传递异常处理函数
val url = "https://www.bennyhuo.com/assets/avatar.jpg" checkUrl(url) asyncBitmap(url, onSuccess = ::show, onError = ::showError)
当异步调用出现异常时,我们调用showError来处理异常的输出,不过这只是对异步调用的异常做了处理。如果url不合法,checkUrl函数抛出了异常呢?或者asyncBitmap内部在启动异步任务时就抛出了未捕获的异常呢?如代码清单1-9所示。
代码清单1-9 完善异常捕获
try { val url = "https://www.bennyhuo.com/assets/avatar.jpg" checkUrl(url) asyncBitmap(url, onSuccess = ::show, onError = ::showError) } catch (e: Exception) { showError(e) }
我们看到,一旦产生了异步调用,异常处理就变得复杂起来了,这里showError被调用了两次,实际生产实践中的情况可能更复杂。
换个角度,异常也是函数调用结果的一种,既然asyncBitmap本身也可能抛出异常,那我们完全可以对抛出的异常与返回的结果一视同仁。如果有一些手段能帮我们把异常的处理合并,我们处理起来就会相对轻松一些。仔细对比图1-4和图1-5,同样存在异步逻辑,只不过后者的异步的调用流程通过编译器或者其他手段简化成了“同步化”的调用,因此前者需要分别处理A到B和C到D处的异常,而后者对整体流程做一次处理即可,复杂度明显降低。
图1-4 异步任务的异常处理
图1-5 同步流程的异常处理
异步逻辑同步化正是Kotlin协程要解决的问题。