简体   繁体   English

Angular:如何从 http 请求列表中返回第一个成功响应

[英]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.我有一个服务器 url 列表,并在循环中向它们发出顺序 http 请求。 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?有人可以建议我如何在 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' });
  }

IMO it's better to avoid using a for loop for subscribing to multiple observables. IMO 最好避免使用for循环来订阅多个 observables。 It might lead to multiple open subscriptions.它可能会导致多个开放订阅。 Common function used for this case is RxJS forkJoin .本案例常用的 function 是 RxJS forkJoin But given your specific condition, I'd suggest using RxJS from function with concatMap operator to iterator each element in order and takeWhile operator to stop based on a condition.但鉴于您的具体情况,我建议使用 function from RxJS 和concatMap运算符来按顺序迭代每个元素,并根据条件使用takeWhile运算符来停止。

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)                        // <-- ignore empty or undefined data (remove this statement if you need them)
    takeWhile((data: string) =>                             // <-- close stream when data is valid and condition is true
      !data || !this.dataIsCorrect(data)
    )
  );
}

Note: Try to tweak the condition inside the takeWhile predicate to match your requirement.注意:尝试调整takeWhile谓词中的条件以匹配您的要求。

In angular we rely on RxJS operators for such complex calls If you want to to call all of them in parallel then once one of them is fulfilled or rejected to cancel the other calls you should use RxJS race learnrxjs.io/learn-rxjs/operators/combination/race Or without RxJS you could use Promise.race In angular we rely on RxJS operators for such complex calls If you want to to call all of them in parallel then once one of them is fulfilled or rejected to cancel the other calls you should use RxJS race learnrxjs.io/learn-rxjs/operators /combination/race 或者没有 RxJS 你可以使用 Promise.race

However if you want to call them in parallel and wait until first fulfilled "not rejected" or all of them rejected this is the case for Promise.any Unfortunately no RxJS operator for it but on the follwoing article you could see how to implement this custom operator for Promise.any and an example for that operator https://tmair.dev/blog/2020/08/promise-any-for-observables/但是,如果您想并行调用它们并等到第一次完成“未拒绝”或全部拒绝,这是 Promise.any 的情况。不幸的是,没有 RxJS 运算符,但在以下文章中,您可以看到如何实现此自定义Promise.any 的运算符和该运算符的示例https://tmair.dev/blog/2020/08/promise-any-for-observables/

You can't use race because it will call all URLs in parallel, but you can use switchMap with recursive implementation您不能使用race ,因为它会并行调用所有 URL,但您可以使用带有递归实现的switchMap

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

create a subject like this创建这样的主题

responseArrived=new Subject();

and after pipe add takeuntil like this并在 pipe 之后像这样添加 takeuntil

 var myObservable = this.queryData(server).pipe(takeUntil(responseArrived),map...

and in the line of code return data just call并在代码行中返回数据只需调用

responseArrived.next()

If your only condition is that you cancel requests once at least one response is received, can't just simply unsubscribe from the observable returned from the HttpClient call?如果您的唯一条件是在收到至少一个响应后取消请求,那么不能简单地取消订阅从HttpClient调用返回的 observable 吗?

  getData() {
    const subscriptions = [];
    [
      'https://reqres.in/api/products/1',
      'https://reqres.in/api/products/2',
      'https://reqres.in/api/products/3',
    ].forEach((url, i) => {
      subscriptions[i] = this.getClientData(url).subscribe(() => {
        // Unsubscribe
        subscriptions.forEach((v, j) => {
          if (j !== i) {
            console.log('Unsubscribe from ', j);
            v.unsubscribe();
          }
        });
      });
    });
  }
  private getClientData(url: string) {
    return this.httpClient.get(url, { responseType: 'text' }).pipe(
      map((response: any) => {
        const data = (response || '').trim();
        if (data && true) return data;
        return null;
      })
    );
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Angular - 如何从 HTTP POST 正确返回 JSON 响应 - Angular - how to properly return JSON response from HTTP POST 从 Angular 中的 http get 返回响应对象 - Return a response object from http get in Angular 如何解决 api 服务器对 angular/typescript 中的 http 请求的响应延迟? - How to account for delay in response from api server on http requests in angular/typescript? 如何以角度返回整个HTTP响应以查看特定的响应头? - How to return the entire HTTP response in angular to view specific response headers? Angular 6 HTTP 处理成功响应(成功代码:200)作为错误响应 - Angular 6 HTTP deals with successful Response (Sucess Code: 200) as error response 如何等待异步HTTP请求在Angular 4上返回值? - How to wait for async HTTP requests to return values on Angular 4? 如何从Angular中的HTTP请求返回JSON响应的特定属性? - How do I return one specific attribute of a JSON response from a HTTP request in Angular? 如何从 angular 中的 Observable/http/async 调用返回响应? - How do I return the response from an Observable/http/async call in angular? Angular 获取响应大小 HTTP 请求 - Angular get response size HTTP requests Angular2:无法返回从Http响应映射的json - Angular2: unable to return a json mapped from an Http response
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM