Flow中的buffer详解

什么是buffer

  • 上游(emit)下游(collect) 之间插入一个有界/无界缓冲队列(底层是 Channel),把两端解耦:上游可以先"塞进队列就走",下游慢慢消费。
  • 主要用于背压处理并发解耦
scss 复制代码
[ emitter ] --(buffer queue)--> [ collector ]

基本用法与参数

ini 复制代码
flow.buffer(
    capacity: Int = Channel.BUFFERED,          // 常用:Channel.BUFFERED(≈64) / UNLIMITED / RENDEZVOUS(0)
    onBufferOverflow: BufferOverflow = SUSPEND // 也可:DROP_OLDEST / DROP_LATEST
)
  • SUSPEND(默认) :队列满了就挂起上游,保证不丢数据。

  • DROP_OLDEST:丢最老的,保最新的一批("滑窗")。

  • DROP_LATEST:丢最新的(更少见)。

  • capacity = 0(RENDEZVOUS):相当于无缓冲,交接点对点同步,不解耦。

  • UNLIMITED:不建议轻易用,可能吃内存。

和其它操作符的区别

  • conflate() ≈ buffer(1, DROP_OLDEST):永远只保"最新值",中间值丢弃,适合"只关心最新状态"的 UI。

  • collectLatest / mapLatest :来了新值会取消前一个下游处理(不是丢缓存,而是取消处理过程),适合"处理很重但只要最新"的场景(比如渲染)。

  • buffer 不会取消处理,只影响排队与丢弃策略

典型场景

  • 上游快、下游慢:日志、传感器、网络流 → buffer(64, SUSPEND)。

  • 只关心最新:搜索框联想、进度流 → conflate() 或 buffer(1, DROP_OLDEST)。

  • 限制突发 + 保持近实时:buffer(n, DROP_OLDEST)(比如 n=8/16)。

与flowOn的关系

  • flowOn(Dispatchers.IO) 自带一个缓冲边界(类似 buffer(Channel.BUFFERED)),使上下文切换处天然解耦。
  • 需要更深队列时,可在 flowOn 之后再加一个 buffer(k) 增大吞吐。
scss 复制代码
upstream
  .map { ioHeavy() }
  .flowOn(Dispatchers.IO) // 这里已形成一个缓冲边界
  .buffer(32)             // 再加一段队列
  .collect { ui() }

放置位置(很关键)

把 buffer() 放在慢操作之前,切开阻塞链条:

scss 复制代码
flow
  .map { fast() }
  .buffer(64)     // 切断:避免后面的慢步骤"顶住"前面
  .map { slow() }
  .collect { consume() }

与 SharedFlow/StateFlow

  • 它们自身有 replay/extraBufferCapacity/onBufferOverflow 。若再套 buffer(),会再多一层队列,只有在确实需要二级缓冲/隔离时才加。

小示例:对比分离前后

scss 复制代码
val f = flow {
  repeat(100) { i ->
    emit(i)             // 生产很快
    delay(10)
  }
}

// 无 buffer:下游慢会直接"顶住"上游
f.collect { delay(50); println(it) }

// 有 buffer:先排队,上游更流畅(满了再按策略处理)
f.buffer(32, onBufferOverflow = BufferOverflow.SUSPEND)
 .collect { delay(50); println(it) }

选择容量的小经验

  • 先用默认(≈64),观察队列积压内存(可埋点 length/max)。

  • 若突发明显且不想丢数据:升大一点,如 128/256。

  • 只要最新:conflate() 或 buffer(1, DROP_OLDEST)。

一句话总结

buffer 用"队列"把快上游与慢下游隔离;SUSPEND 保正确性,DROP_OLDEST 保实时性;把 buffer 放在慢步骤之前最有效。

相关推荐
假如让我当三天老蒯1 天前
前端跨域解决方案(学习用)
前端·javascript·面试
Colin草率地做慢慢地改1 天前
关于QuickStore这个项目的重构(2)- 数据库建表文件
后端·面试·架构
JieE2121 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
JustHappy2 天前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom2 天前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
假如让我当三天老蒯2 天前
模块化:ES Module 与 CommonJS 的区别
前端·面试
沉默王二2 天前
面试官:RAG 不用向量数据库,用 MySQL 硬扛?我:100 万向量不是很轻松?
mysql·面试·ai编程
Darling噜啦啦3 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
swipe3 天前
正则表达式入门到进阶:从表单校验到手写模板引擎
前端·javascript·面试