在本系列的第一篇文章里,我們解釋了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.