toAsync

Eleva un Iterable normal —de valores o de Futures— a un FxAsyncIterable, la puerta de entrada al pipeline asíncrono de FxDart.

FxAsyncIterable<T> toAsync<T>(Iterable<FutureOr<T>> iterable) FxAsync<T> Fx.toAsync<T>() // chain, on a sync Fx FxAsync<T> Iterable<FutureOr<T>>.fxAsync // method (resolves the futures)

Lección

Todo pipeline asíncrono de FxDart empieza con toAsync. Toma un Iterable<FutureOr<T>> normal —una lista de valores simples, una lista de Futures o una mezcla— y lo envuelve en un FxAsyncIterable, el tipo que entienden todos los operadores *Async y la cadena FxAsync. Cada vez que un elemento resulta ser un Future, se espera automáticamente conforme se extrae.

FxAsyncIterable está basado en pull: nada se ejecuta hasta que un terminal (toListAsync, eachAsync, el .toList() de la cadena FxAsync, …) llama a next() sobre él, un paso cada vez — exactamente igual que un Iterable normal, solo que asíncrono. Es un alejamiento deliberado del Stream de Dart, que está basado en push: en cuanto un stream empieza a emitir, es él quien decide el ritmo, y un consumidor aguas abajo no tiene forma de decirle «evalúa 3 de estos a la vez». El protocolo next([Concurrent? concurrent]) de FxDart añade justo ese canal de retorno: un operador aguas abajo como concurrent(n) puede pasar un marcador aguas arriba con cada pull, pidiéndole a la fuente que ejecute n elementos en paralelo. Los Streams no ofrecen ningún punto de enganche equivalente, y esa es justo la razón por la que FxDart define su propio iterable asíncrono en vez de construir sobre Stream.

Usa el toAsync(iterable) de nivel superior para un Iterable<FutureOr<T>> en crudo, o el método de cadena fx(iterable).toAsync() para pasar una cadena Fx existente a su equivalente FxAsync. Ambos son perezosos: construir el pipeline no hace nada hasta que algo tira de él.

Demo 1 · Valores, Futures y la forma encadenada

toAsync acepta valores simples, Futures o una mezcla de ambos — y la forma encadenada hace lo mismo a partir de un Fx existente:

Demo 2 · Por qué importa el modelo basado en pull

En Dart, un Future empieza a ejecutarse en el instante en que se crea, no cuando se espera. Así que tres Futures construidos de forma ansiosa en un literal de lista ya se están ejecutando en paralelo antes de que toAsync los toque siquiera. Compáralo con mapAsync (o el .map de la cadena), que crea un Future nuevo por elemento solo cuando se tira de él — de forma perezosa, uno a uno, salvo que añadas concurrent(n):

La forma con método

xs.fxAsync es esta función como getter. Devuelve un FxAsync y no un FxAsyncIterable pelado, así que la cadena puede continuar — y resuelve los futuros, cosa que xs.fx no hace.

await responses.fxAsync.map(parse).concurrent(4).toList();

// responses.fx would be an Fx<Future<T>> — a chain over the
// futures rather than their values.

El nombre lleva fx a propósito: toAsync es una expresión lo bastante general como para no decir, sobre un Iterable pelado, en qué librería se entra. Las formas con getter están recogidas en fx.

Pruébalo tú

Ejercicio: filtra la lista de abajo para que solo las notas aprobadas (>= 60) lleguen al resultado.

Relacionado: convención de nombres *Async — mapAsync, filterAsync, … · puentes con Stream — fromStream, fxStream, toStream · concurrent — el canal de retorno en acción · delay & sleep — para construir demos asíncronas