fork

Ramifica una única iteración con buffer de una fuente en lectores independientes y reproducibles.

Iterable<T> fork<T>(Iterable<T> iterable) FxAsyncIterable<T> forkAsync<T>(FxAsyncIterable<T> iterable)

Lección

Iterar dos veces el mismo objeto Iterable de Dart normalmente ejecuta su fuente dos veces — un generador sync* vuelve a empezar desde cero cada vez que pides un .iterator nuevo. Eso es un derroche (o directamente un error) cuando producir un valor sale caro: una petición de red, un cálculo lento, un stream que solo puedes leer una vez. fork lo arregla: cada llamada a fork(iterable) con el mismo objeto iterable devuelve un cursor independiente sobre un único buffer compartido que crece de forma perezosa. La fuente subyacente se recorre exactamente una vez, sin importar cuántos forks lean de ella ni en qué orden.

La compartición se indexa por la identidad del iterable que pasas (mediante un Expando interno), así que debes hacer fork del mismo objeto — no de dos iterables construidos por separado que casualmente se parezcan. Cada fork puede consumirse a su propio ritmo: adelantarse en un fork tira de nuevos valores de la fuente y los añade al buffer compartido; un fork que va rezagado simplemente reproduce los valores que ya están en el buffer, sin coste adicional. forkAsync funciona igual para FxAsyncIterable y, además, permite que la demanda concurrente de varios forks aguas abajo tire de la fuente asíncrona compartida en paralelo.

Demo 1 · Una fuente, dos ramas, demostrado con un contador

source() incrementa calls cada vez que produce un valor. Tanto evens como doubled hacen fork del mismísimo objeto shared — si la fuente se ejecutara dos veces, calls acabaría en 10, no en 5:

Demo 2 · Forks a ritmos distintos comparten un solo buffer

La rama a se adelanta y tira de dos valores nuevos; cuando la rama b pide sus dos primeros, simplemente reproduce lo que a ya había almacenado — sin nuevas llamadas a source() hasta que b necesita un tercer valor que ningún fork ha visto todavía:

Pruébalo tú

Ejercicio: ahora mismo readings se itera dos veces sin fork, así que sensor() se ejecuta dos veces y reads acaba en 6. Haz fork de readings para cada consumidor de modo que el sensor se lea una sola vez (reads debería ser 3).

Relacionado: peek — observar sin ramificar · concurrent — evaluación paralela dentro de una sola rama · memoize — cachear un único valor en lugar de una secuencia entera