它们的函数注释一言难尽,建议别看,onCompletion的注释不太对。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| fun main() = runBlocking<Unit> { (1..3).asFlow() .catch { cause: Throwable -> println("0 catch ${cause}") } .onCompletion { cause: Throwable? -> if (cause == null) { println("0 Done successfully") } else { println("0 Done fail") }
} .onEach { value -> check(value <= 1) { "Crash on $value" } println("Got $value") } .onCompletion { cause: Throwable? -> if (cause == null) { println("1 Done successfully") } else { println("1 Done fail") } } .catch { cause: Throwable -> println("1 catch ${cause}") } .onCompletion { cause: Throwable? -> if (cause == null) { println("2 Done successfully") } else { println("2 Done fail") } } .collect() }
输出结果
Got 1 0 Done fail 1 Done fail 1 catch java.lang.IllegalStateException: Crash on 2 2 Done successfully
|
catch
- 只能捕捉到上游的异常,下游无法(所以它根本不能捕获终端操作符中抛出的异常)。
- 只有捕捉到异常,才进入代码块。
- 可以抛出其他异常
- 可以emit值
onCompletion
这个有点复杂,分情况讨论。
正常结束
- onCompletion操作符按声明顺序,依次进入其代码块执行,异常参数为null
- 可以emit值
异常结束
异常结束的时候,一个onCompletion操作符异常参数依然有可能为null。
- 为null的情况是:该操作符和异常抛出处之间(注意之间),有catch捕获了该异常,且catch没有抛出异常,且终端操作符没有抛出异常。
- 其他情况,异常参数都不为null。
当异常参数不为null的情况下(不管它正常结束还是异常结束)才可以在代码块中emit值。
举个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| fun main() = runBlocking<Unit> { (1..3).asFlow() .onCompletion { cause: Throwable? -> if (cause == null) { println("0 Done successfully") } else { println("0 Done fail") } } .onEach { value -> check(value <= 1) { "Crash on $value" } println("Got $value") } .onCompletion { cause: Throwable? -> if (cause == null) { println("1 Done successfully") } else { println("1 Done fail") } emit(100) } .catch { cause: Throwable -> println("1 catch ${cause}") } .onCompletion { cause: Throwable? -> if (cause == null) { println("2 Done successfully") } else { println("2 Done fail") } } .collect{} }
|
上面的示例已异常结束,0/1处异常参数不为空,2处为空(catch把它捕获了)。
如果终端有异常,所有onCompletion操作符异常参数都不为空。
另外,catch和onCompletion的执行顺序按照声明顺序(如果某catch可以执行的话)。
声明式写法
1 2 3 4 5 6 7 8 9 10 11 12
| fun main() = runBlocking<Unit> { (1..3).asFlow() .onEach { value -> check(value <= 1) { "Crash on $value" } println("Got $value") }.onCompletion { println("Done") }.catch { e -> println("Caught $e") }.collect() }
|
- 为了让catch可以捕获所有异常,把catch放到collect之前,collect留空,业务代码写在onEach中。
- onCompletion一定可以执行,上面的写法中,如果上游有异常,异常参数是会不为null,但是我们不要管它,留给catch来处理。这里的执行顺序是onCompletion再catch。
1 2 3 4 5 6 7 8 9 10 11 12
| fun main() = runBlocking<Unit> { (1..3).asFlow() .onEach { value -> check(value <= 1) { "Crash on $value" } println("Got $value") }.catch { e -> println("Caught $e") }.onCompletion { println("Done") }.collect() }
|
调换位置。
第一个写法是catch可以捕获到onCompletion的异常,第二种不行。第二种先catch再onCompletion。
抽出一个拓展函数和用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| fun main() { CoroutineScope(Dispatchers.Default).launch { flow<Int> { emit(1) emit(2) emit(3) }.catchCompletionCollect( action = { println("Got $it") if (it == 2) { println("throw error") throw IllegalStateException() } }, doWhenFlowCompleteSuccessful = { println("做一些完全成功才做的事") }, doWhenFlowMeetError = { println("meet error: $it") println("做一些发生错误才做的事") } ) } Thread.sleep(99999) }
suspend fun <T> Flow<T>.catchCompletionCollect( action: (T) -> Unit, doWhenFlowCompleteSuccessful: () -> Unit, doWhenFlowMeetError: (Throwable) -> Unit ) { this.onEach { action(it) } .onCompletion { cause: Throwable? -> if (cause == null) { doWhenFlowCompleteSuccessful() } } .catch { doWhenFlowMeetError(it) } .collect() }
|
参考资料:medium