Tengo una lista de URL de servidores y les hago solicitudes http secuenciales en un bucle. Cuando llega la respuesta exitosa de la solicitud actual, quiero romper el bucle y no llamar a todos los demás servidores. ¿Alguien podría aconsejarme cómo podría manejarse esto en Angular/RxJS? Algo como:
getClientData() { for(let server of this.httpsServersList) { var myObservable = this.queryData(server) .pipe( map((response: any) => { const data = (response || '').trim(); if(data && this.dataIsCorrect(data)) { return data; // **here I want to break from the loop!** } }) ); return myObservable; } } private queryData(url: string) { return this.http.get(url, { responseType: 'text' }); }En mi opinión, es mejor evitar usar un ciclo for para suscribirse a múltiples observables. Podría dar lugar a múltiples suscripciones abiertas. La función común utilizada para este caso es RxJS forkJoin . Pero dada su condición específica, sugeriría usar RxJS from la función con el operador concatMap para iterar cada elemento en orden y el operador takeWhile con su argumento inclusive establecido en true (gracias @Chris) para detenerse en función de una condición y devolver el último valor .
import { from } from 'rxjs'; import { concatMap, filter, map, takeWhile } from 'rxjs/operators'; getClientData(): Observable<any> { return from(this.httpsServersList).pipe( concatMap((server: string) => this.queryData(server)), map((response: any) => (response || '').trim()), filter((data: string) => !!data && this.dataIsCorrect(data)) // <-- ignore empty or undefined and invalid data takeWhile(((data: string) => // <-- close stream when data is valid and condition is true !data || !this.dataIsCorrect(data) ), true) ); } Nota: intente ajustar la condición dentro del predicado takeWhile para que coincida con su requisito.
Edición 1: agregue un argumento inclusive en takeWhile opeartor
Edición 2: agregue una condición adicional en el operador de filter
En angular, confiamos en los operadores RxJS para llamadas tan complejas. Si desea llamarlos a todos en paralelo, una vez que se cumpla o rechace uno de ellos para cancelar las otras llamadas, debe usar la carrera RxJS learnrxjs.io/learn-rxjs/operators /combination/race O sin RxJS podrías usar Promise.race
Sin embargo, si desea llamarlos en paralelo y esperar hasta que se cumpla el primer "no rechazado" o hasta que todos sean rechazados, este es el caso de Promise.any Desafortunadamente, no hay operador RxJS para ello, pero en el siguiente artículo puede ver cómo implementar esta costumbre. operador para Promise.any y un ejemplo para ese operador https://tmair.dev/blog/2020/08/promise-any-for-observables/
No puede usar race porque llamará a todas las URL en paralelo, pero puede usar switchMap con implementación recursiva
import { of, Observable, throwError } from 'rxjs'; import { catchError, switchMap } from 'rxjs/operators' function getClientData(urls: string[]) { // check if remaining urls if (!urls.length) throw throwError(new Error('all urls have a error')); ; return queryData(urls[0]).pipe( switchMap((response) => { const data = (response || '').trim(); if(data && this.dataIsCorrect(data)) // if response is correct, return an observable with the data // for that we use of() observable return of(data) // if response is not correct, we call one more time the function with the next url return getClientData(urls.slice(1)) }), catchError(() => getClientData(urls.slice(1))) ); } function queryData(url: string): Observable<unknown> { return this.http.get(url, { responseType: 'text' }); }