javascriptangularrxjs

Angular: how to return the first successful response from the list of http requests


I have a list of servers urls and making sequential http requests to them in a loop. When the success response arrives from the current request I want to break the loop and not to call all other servers. Could someone advice me how this could be handled in Angular/RxJS? Something like:

  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' });
  }

Solution

  • IMO it's better to avoid using a for loop for subscribing to multiple observables. It might lead to multiple open subscriptions. Common function used for this case is RxJS forkJoin.

    But given your specific condition, I'd suggest using

    1. RxJS from function with concatMap operator to iterate each observable in order
    2. filter operator to ignore empty or undefined and invalid data.
    3. take operator with count argument set to 1 to close the observable as soon a valid data is emitted (thanks @Akxe in comments).
    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
        take(1)
      );
    }
    

    Note: Try to tweak the condition inside the filter operator to match your requirement.

    Edit 1: add inclusive argument in takeWhile opeartor

    Edit 2: add additional condition in the filter operator

    Edit 3: replace takeWhile with take operator (thanks @Akxe in comments)