Me gustaría repetir una llamada API que devuelve una Promesa, condicionalmente usando rxjs.
El método API recibe una identificación que se cambiará en cada llamada al agregarle un prefijo de contador. Las llamadas se repetirán hasta que los datos cumplan alguna condición o el contador llegue a un número específico X. ¿Cómo se puede hacer usando rxjs?
Método API:
fetchData(id):Promise<data>
prueba 1: fetchData (id)
prueba 2: buscar datos (id_1)
prueba 3: buscar datos (id_2)
En mi opinión, es mejor manejar el sondeo a través de Promises o RxJS sin mezclarlos. Ilustraría usando RxJS.
Prueba lo siguiente
from
RxJS.timer
o interval
para emitir regularmente un valor en un intervalo fijo.switchMap
para mapear desde la emisión externa a su llamada API. Consulte aquí para obtener una breve descripción sobre los diferentes tipos de operadores de mapeo de orden superior.takeWhile
, uno para cada una de sus condiciones respectivamente, para completar la suscripción.filter
para enviar solo las emisiones que pasan la condición. import { from } from 'rxjs'; fetchData(id: any): Observable<any> { // <-- return an observable return from(apiCall); // <-- use `from` to convert Promise to Observable }
import { timer } from 'rxjs'; import { filter, switchMap, takeWhile } from 'rxjs/operators'; timer(0, 5000).pipe( // <-- poll every 5 seconds takeWhile((index: number) => index < 20) // <-- stop polling after 20 attempts switchMap((index: number) => this.someService.apiCall(index+1) // <-- first emission from `timer` is 0 ), takeWhile( // <-- stop polling when a condition from the response is unmet (response: any) => response.someValue !== someOtherValue, true // <-- emit the response that failed the test ), filter((response: any) => response.someValue === someOtherValue // <-- forward only emissions that pass the condition ) ).subscribe({ next: (response: any) => { // handle response }, error: (error: any) => { // handle error } });
Editar: la condición en el takeWhile
estaba haciendo lo contrario del requisito. Ajusté la condición e incluí el argumento inclusive=true
. Gracias @Siddhant en los comentarios.
Puede usar concatMap para asegurarse de que solo se intente una llamada a la vez. range
proporciona el número máximo de llamadas porque takeWhile
cancelará la suscripción anticipadamente (antes de que finalice el rango) si se cumple/no se cumple una condición.
Eso podría verse así:
// the data met some condition function metCondition(data){ if(data/*something*/){ return true; } else { return false } } // the counter reach to a specific number X const x = 30; range(0, x).pipe( concatMap(v => fetchData(`id_${v === 0 ? '' : v}`)), takeWhile(v => !metCondition(v)) ).subscribe(datum => { /* Do something with your data? */ });
Podría intentar reintentar cuando:
let counter=0; const example = of(1).pipe( switchMap(x => of(counter)), // Replace of() with from(fetchData('id_'+counter)) map(val => { if (val < 5) { counter++; // error will be picked up by retryWhen throw val; } return val; }), retryWhen(errors => errors.pipe( // log error message tap(val => console.log(`Response was missing something`)), ) ) );
No es ideal ya que necesita un contador en el ámbito externo, pero hasta que haya una solución mejor (especialmente sin reintentos basados en el tiempo), esto debería funcionar.