原文:Flow: an intro for an RxJava user
作者:Mohamed Ibrahim
译者:Fly_with24
1 2 3 4 5 6 | listOf("Madara", "Kakashi", "Naruto", "Jiraya", "Itachi") .map { it.length } .filter { it > 4 } .forEach { println(it) } |
在此示例中,如果您深入研究 map 函数源代码,您将发现这里没有魔法,它只是列表的循环,进行了一些转换然后为您提供了一个新列表。 过滤器也一样。 这种机制称为
1 2 3 4 5 6 7 8 9 | listOf("Madara", "Kakashi", "Naruto", "Jiraya", "Itachi") // 使用 Sequence .asSequence() .map { it.length } .filter { it > 4 } .forEach { println(it) } |
这里的区别就是先调用
flow
如果我们尝试获取列表并将其用作 flow ,并在流的末尾调用
流也是
因此,Collections 扩展功能仅适用于小数据,
构建 flow
我们看到
1 2 3 4 5 | public fun <T> Iterable<T>.asFlow(): Flow<T> = flow { forEach { value -> emit(value) } } |
如果我们要编写前面的示例在数据源中添加一些逻辑,则只需使用
转换操作符
flow 拥有一些列的用于转换的运算符,例如
在由 Coroutines 提供支持的
这里想表达的是 flow 具有更简单的设计,并且与以其陡峭的学习曲线而闻名的
terminal 操作符
我已经提到
flow 中的 terminal operator 是需要作用域操作的挂起函数,其他的 operator 例如
-
toList(),toSet -> 返回集合中的所有 item
-
first() -> 仅返回第一个发射
-
reduce(),fold() -> 使用特定操作获取结果
发射数据
为了发射数据,您需要使用一个挂起函数
1 2 3 4 5 | //fire a coroutine someScope.launch { //fire flow with a terminal operator flowSampleData().collect { } } |
上面的花括号让人想起了回调,您可以使用
1 2 3 4 5 | flowSampleData() .onEach { //handle emissions } .launchIn(someScope) |
取消
每次设置
对于 flow 使用特定 scope 的协程则可以无需进行额外的工作来达到此目的
错误处理
让我们模拟一个错误
1 2 3 4 5 6 7 8 9 | private fun flowOfAnimeCharacters() = flow { emit("Madara") emit("Kakashi") // 抛出异常 throw IllegalStateException() emit("Jiraya") emit("Itachi") emit("Naruto") } |
使用
1 2 3 4 5 6 7 8 | runBlocking { flowOfAnimeCharacters() .map { stringToLength(it) } .filter { it > 4 } .collect { println(it) } } |
如果我们运行此代码,它将引发异常,并且如我们所说,您有两个选项可以处理错误,即常规
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 使用 try-catch runBlocking { try { flowOfAnimeCharacters() .map { stringToLength(it) } .filter { it > 4 } .collect { println(it) } } catch (e: Exception) { println(e.stackTrace) } finally { println("Beat it") } } |
1 2 3 4 5 6 7 8 9 10 11 | // 使用 catch{} runBlocking { flowOfAnimeCharacters() .map { stringToLength(it) } .filter { it > 4 } // catch .catch { println(it) } .collect { println(it) } } |
使用
恢复
如果错误中断了流,并且我们打算使用完整备份或默认数据恢复流,在
1 2 3 4 5 6 7 8 9 | runBlocking { flowOfAnimeCharacters() .catch { emitAll(flowOf("Minato", "Hashirama")) } .collect { println(it) } } |
那么得到的结果是
1 2 3 4 | Madara Kakashi Minato Hashirama |
flowOn()
默认情况下,flow 数据源将在调用者上下文中运行,如果要更改它,例如,要使 flow 在 IO 而不是 Main 上运行,则使用
这里的
完成
当 flow 完成发射时,您可能需要执行一些操作,
已知数据源如下
1 2 3 4 5 6 7 8 | private fun flowOfAnimeCharacters() = flow { emit("Madara") emit("Kakashi") throw IllegalStateException() emit("Jiraya") emit("Itachi") emit("Naruto") } |
答案是否定的,catch 捕获了所有错误,接下来是全新的事情,请记住
总结
您可以使用 Flow builders 构建 flow,其中最基本的是
关于我
我是 Fly_with24
- 掘金
- 简书
- Github