kotlin Flow first() last()总结

一、核心函数定义

以下是 first()/firstOrNull()/last()/lastOrNull() 的核心逻辑,其中 last 系列以「Flow 的 block 执行完毕」为核心判断依据:

函数 核心行为(无 predicate) 带 predicate 时的行为 异常/边界处理
first() 触发 Flow 收集,拿到发射的第一个元素后立即取消收集(无需等待 block 执行完毕) 拿到第一个满足条件的元素后立即取消收集 空 Flow/无满足条件的元素 → 抛出 NoSuchElementException
firstOrNull() 与 first() 逻辑完全一致 与 first() 逻辑完全一致 空 Flow/无满足条件的元素 → 返回 null(不抛出异常)
last() 触发 Flow 收集,等待 Flow 的整个 block 执行完毕后,取发射的最后一个元素 等待 Flow 的 block 执行完毕后,取最后一个满足条件的元素 空 Flow/无满足条件的元素 → 抛出 NoSuchElementException;block 无限执行 → 永久挂起
lastOrNull() 与 last() 逻辑完全一致 与 last() 逻辑完全一致 空 Flow/无满足条件的元素 → 返回 null;block 无限执行 → 永久挂起

关键说明:

  • 所有函数均为 Flow 的终端操作符,调用后会立即触发 Flow 收集;
  • predicate 仅改变"筛选元素的规则",不改变"取第一个/最后一个""是否等待 block 执行完毕"的核心逻辑。

二、不同 Flow 类型下的行为差异

1. 普通 Flow(冷流)

普通 Flow 由 flow { ... } 定义,此处的 { ... } 即为该 Flow 的 block,每次调用终端操作都会重新执行该 block:

函数 block 可执行完毕的有限 Flow(如 flow { emit(1); emit(2); emit(3) }) block 无限执行的无限 Flow(如 flow { while(true) { emit(1) } })
first()/firstOrNull() 正常拿到第一个(满足 predicate)元素,拿到后立即取消 block 执行 正常拿到第一个(满足 predicate)元素,拿到后立即取消 block 执行
last()/lastOrNull() 等待 block 完全执行完毕,正常返回最后一个(满足 predicate)元素 block 无限执行,函数永久挂起,无返回结果

示例验证:

kotlin 复制代码
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // 有限普通 Flow(block 可执行完毕)
    val finiteFlow = flow {
        emit(1)
        delay(10)
        emit(2)
        delay(100)
        emit(3) // block 执行完毕
    }
    println(finiteFlow.first())  // 输出 1(立即返回,取消 block 执行)
    println(finiteFlow.last())   // 输出 3(等待 block 执行完毕后返回)

    // 无限普通 Flow(block 无限执行)
    val infiniteFlow = flow {
        var i = 0
        while (true) { // 无限循环,block 永不结束
            emit(i++)
            delay(100)
        }
    }
    println(infiniteFlow.first()) // 输出 0(立即返回)
    // println(infiniteFlow.last()) // 永久挂起,无输出
}
2. StateFlow/SharedFlow(热流)

StateFlow/SharedFlow 无用户自定义的 flow { ... } block,其内部维护了永久活跃的发射逻辑(可理解为内部 block 无限执行),且为热流(创建后持续活跃,无需每次收集重新执行逻辑):

函数 StateFlow(如 MutableStateFlow(0)) SharedFlow(如 MutableSharedFlow<Int>())
first()/firstOrNull() 立即拿到当前最新值(StateFlow 初始值/已更新值),拿到后取消收集 无缓存值时等待第一个值发射,拿到后取消收集;有缓存值时直接拿第一个缓存值
last()/lastOrNull() 内部 block 无限执行,函数永久挂起;手动取消协程则抛出 CancellationException 内部 block 无限执行,函数永久挂起;手动取消协程则抛出 CancellationException

示例验证:

kotlin 复制代码
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // StateFlow 示例
    val stateFlow = MutableStateFlow(0)
    println(stateFlow.first()) // 输出 0(立即拿到初始值,取消收集)
    stateFlow.value = 1
    // println(stateFlow.last()) // 永久挂起,无输出

    // SharedFlow 示例(无初始缓存)
    val sharedFlow = MutableSharedFlow<Int>()
    launch { delay(100); sharedFlow.emit(1) }
    println(sharedFlow.first()) // 等待 100ms 输出 1(拿到后取消收集)
    // println(sharedFlow.last()) // 永久挂起,无输出
}

总结

  1. first()/firstOrNull() 核心是「拿第一个元素即终止收集」,无需等待 block 执行完毕,对所有类型 Flow 均能正常返回(SharedFlow 无缓存时仅需等待第一个值发射);
  2. last()/lastOrNull() 核心是「等 Flow 的 block 执行完毕再拿最后一个元素」,仅适用于 block 可执行完毕的有限普通 Flow,对无限普通 Flow/StateFlow/SharedFlow 会永久挂起;
  3. 实际开发中优先使用 firstOrNull()/lastOrNull(),避免空 Flow/无满足条件元素时抛出异常。
相关推荐
aqiu11111140 分钟前
【C++算法打怪专栏】8. 字符串转换整数 (atoi)
开发语言·c++·算法
爱吃香菜的初学者1 小时前
十四.Linux——消息队列
linux·运维·开发语言
迅猛龙办公室1 小时前
python实现倒序输出数字
开发语言·python
仿生狮子4 小时前
OpenAI 攻下千禧年难题之后,开发者似乎无动于衷
前端·数学·vibecoding
夏幻灵7 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
Lvan的前端笔记7 小时前
docker:每个前端项目一个 Nginx 容器还是只有一个Nginx容器
前端·nginx·docker
程序猿编码7 小时前
榨干 RTX5090 算力!Qwen3 专用单卡推理引擎,手写C++/CUDA 算子实现 MTP 推测解码
开发语言·c++·大模型推理·qwen3·多模态推理
三天不学习7 小时前
Egg.js 4 突然爆火,原因是否归结于AI 原生落地需求爆发?
前端·javascript·全栈·egg.js
墨天梦8 小时前
B19_SunnyWeather到阅读客户端
android·kotlin
xcs194058 小时前
前端 vue 的前端页面debugger 进不去
前端·javascript·vue.js