angular - El operador de expansión se ejecuta más veces de lo esperado
Estoy tratando de crear un código que pagine un conjunto de resultados usando el operador expand y lo haga hasta que se haya obtenido un cierto número de recursos. Esto es lo que tengo hasta ahora (he eliminado la lógica de llamada asíncrona real):
import { Observable } from 'rxjs';
const items = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
];
const call = () => {
console.log('#call');
const batch = items.shift();
if (batch) {
return Observable.of(batch).delay(100);
}
return Observable.empty();
}
const o$ = call().expand((values) => {
console.log('expansion');
return call();
}).flatMap((val: any) => val);
const log = (prefix: string) => (...args: any[]) => console.log(prefix, ...args);
o$.take(9).subscribe(log('next'), log('error'), log('complete'));
La salida es diferente de lo que quisiera:
#call
takeWhile.ts:26 next 1
takeWhile.ts:26 next 2
takeWhile.ts:26 next 3
takeWhile.ts:26 next 4
takeWhile.ts:26 next 5
takeWhile.ts:26 next 6
takeWhile.ts:26 next 7
takeWhile.ts:26 next 8
takeWhile.ts:26 next 9
takeWhile.ts:26 complete
takeWhile.ts:22 expansion
takeWhile.ts:10 #call
Obtengo mis 9 elementos, que es lo que solicité usando take, luego el flujo se completa, pero ocurre una expansión adicional y la API asíncrona simulada se llama una vez más.
¿Hay alguna manera de hacer que mi código sea menos codicioso y no inicie otra iteración recursiva?
Respuesta #1
El comportamiento que estás viendo es esperado: expand se llama antes de take, así que
- obtenemos el primer valor
- expand se ejecuta y se realiza la llamada
- entonces todo el observable se desuscribe inmediatamente
Por lo tanto, necesitas hacer que la llamada recursiva sea asincrónica para permitir que el take se desuscriba antes de que se haga esa llamada. La solución más sencilla parece ser sustituir return call(); por return timer(4).map(call); (o incluso un return defer(call);, que funciona mágicamente :/ ).
Ver este ejemplo en acción
Así que, en general, necesitas hacer una "pausa"/"intervalo" para dejar que el limitador actúe y darse de baja.
Espero que esto ayude