在本系列的第一篇文章里,我们解释了flow{}构造器的一个限制:它无法接收在运行collect()的协程之外产生的值。当数据来自回调、遗留组件或者不遵守结构化并发的任务时,这个限制就会在真实应用中暴露出来。这时,flow{}不能安全地发送值,我们需要一个不同的机制。
这一篇聚焦于channelFlow。我们不从API的角度去描述它,而是动手构建一个简化版本,并借助这个版本去理解真正的构造器是如何工作的。这么做可以绕开对Flow内部实现细节的依赖,直接展示这个机制为什么必要,又为什么有效。
![]()
如果你是跳读到本文的,建议先回去看第一部分。那里会介绍一个极简的心智模型,解释channelFlow要解决的问题。缺少那个上下文,下面的行为可能会让你觉得意外。
这篇文章的目标是建立实用的理解。我们会看到,一个自定义的实现如何既能接收来自其他协程的值,又完全遵守Flow的规则。这也为下一篇做好铺垫——届时我们会讨论callbackFlow,看看它如何把同样的思路扩展到基于回调的API上。
在构建channelFlow的简化版之前,需要先明确它要解决的问题。需求并不复杂:值可能是在不运行collect()的协程里产生的,但Flow仍然要求所有发送操作必须发生在执行收集的那个协程里。普通的flow{}无法满足这个要求。
来看看一个典型的Flow收集过程:
viewModelScope.launch {
someFlow.collect { value ->
process(value)
}
}
启动collect()的那个协程成了生产者和收集者都必须运行的地方。任何从其他协程发出的emit()调用都会破坏Flow的执行模型。第一篇文章用了一个简单的并行示例来演示这一点。在实际应用中,值可能来自长时间运行的后台工作、监听器,或者我们无法控制的组件。
这就产生了一个现实需求:我们必须能够从其他协程接收值,同时还要在正确的位置把这些值交给收集者。换句话说,我们需要一个边界,将值的生产与向收集者交付值的过程分开。Flow默认并不提供这样的边界。这正是channelFlow要填补的空缺。
为了看清怎么做,我们会构建一个最小版本的机制。目标不是把标准库里的完整实现复现一遍,而是呈现让channelFlow能够运转的核心想法。一旦这个想法变得清晰,真正构造器里的行为也就更好理解了。
接下来的思路很简单:用通道把生产和发送分开。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.