2. Separando a produção da emissão com um canal
O Flow exige que o emit() seja executado na corrotina que executa collect(). Quando valores são criados em outras corrotinas, precisamos de uma maneira segura para retornar esses valores à corrotina coletora sem violar essa regra. Um canal nos dá essa possibilidade. Uma corrotina pode enviar valores no canal e outra corrotina pode recebê-los. Se o lado receptor rodar dentro do bloco flow { }, o emit() permanece na posição correta e o comportamento do Flow é mantido consistente.
A ideia pode ser demonstrada com um pequeno exemplo. Dentro do bloco flow { } criamos um canal e lançamos várias corrotinas que preparam valores e os enviam no canal. O loop que recebe valores do canal roda na mesma corrotina que executa collect(), então pode chamar o emit() de forma segura.
@OptIn(ExperimentalCoroutinesApi::class)
fun diyChannelFlow(): Flow<Int> = flow {
coroutineScope {
val channel = produce<Int> {
launch {
delay(100)
send(1)
}
launch {
delay(100)
send(2)
}
}
for (value in channel) {
emit(value)
}
}
}Se coletarmos este fluxo:
diyChannelFlow().collect { value ->
println("Collected: $value")
}a saída será:
Collected: 1
Collected: 2A ordem dos valores depende da agendamento. Ambos os valores são preparados em paralelo, então ou 1 ou 2 pode ser impresso primeiro.
Este exemplo mostra a ideia-chave por trás do mecanismo. O trabalho é feito em corrotinas separadas, mas a emissão acontece apenas na corrotina que coleta o fluxo. Esta separação é a base da channelFlow. Ao mesmo tempo, este exemplo é intencionalmente minimalista. Ele não gerencia o fechamento do canal, cancelamento, buffer ou consumidores múltiplos. O construtor real lida com essas preocupações internamente. Na próxima seção refinaremos o exemplo para trazer mais próximo ao comportamento da channelFlow e preparar a base para entender a callbackFlow.

