Angular: 如何return 从http 请求列表中第一个成功响应
Angular: how to return the first successful response from the list of http requests
我有一个服务器 url 列表,并在循环中向它们发出连续的 http 请求。当成功响应从当前请求到达时,我想打破循环而不是调用所有其他服务器。有人可以建议我如何在 Angular/RxJS 中处理吗?类似于:
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' });
}
在 angular 中,我们依赖 RxJS 运算符进行此类复杂调用
如果您想同时调用所有这些,那么一旦其中一个被满足或拒绝以取消您应该使用的其他调用
RxJS 竞赛 learnrxjs.io/learn-rxjs/operators/combination/race
或者没有 RxJS 你可以使用 Promise.race
但是,如果您想并行调用它们并等到第一次满足“未拒绝”或所有这些都被拒绝,Promise.any 就是这种情况
不幸的是,它没有 RxJS 运算符,但在下面的文章中,您可以看到如何为 Promise.any 实现此自定义运算符以及该运算符的示例
https://tmair.dev/blog/2020/08/promise-any-for-observables/
您不能将 race because it will call all URLs in parallel, but you can use 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' });
}
创建这样的主题
responseArrived=new Subject();
并在管道后像这样添加 takeuntil
var myObservable = this.queryData(server).pipe(takeUntil(responseArrived),map...
并且在代码行return数据中调用
responseArrived.next()
IMO 最好避免使用 for
循环来订阅多个可观察对象。它可能会导致多个开放订阅。这种情况下使用的常用函数是 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
运算符,其 inclusive
参数设置为 true
(感谢@Chris)以根据条件停止并 return 最后一个值.
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)
);
}
注意:尝试调整 takeWhile
谓词中的条件以满足您的要求。
编辑 1:在 takeWhile
opeartor
中添加 inclusive
参数
编辑 2:在 filter
运算符中添加附加条件
如果您的唯一条件是在至少收到一个响应后取消请求,难道不能简单地取消订阅从 HttpClient
调用返回的可观察对象吗?
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;
})
);
}
我有一个服务器 url 列表,并在循环中向它们发出连续的 http 请求。当成功响应从当前请求到达时,我想打破循环而不是调用所有其他服务器。有人可以建议我如何在 Angular/RxJS 中处理吗?类似于:
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' });
}
在 angular 中,我们依赖 RxJS 运算符进行此类复杂调用 如果您想同时调用所有这些,那么一旦其中一个被满足或拒绝以取消您应该使用的其他调用 RxJS 竞赛 learnrxjs.io/learn-rxjs/operators/combination/race 或者没有 RxJS 你可以使用 Promise.race
但是,如果您想并行调用它们并等到第一次满足“未拒绝”或所有这些都被拒绝,Promise.any 就是这种情况 不幸的是,它没有 RxJS 运算符,但在下面的文章中,您可以看到如何为 Promise.any 实现此自定义运算符以及该运算符的示例 https://tmair.dev/blog/2020/08/promise-any-for-observables/
您不能将 race because it will call all URLs in parallel, but you can use 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' });
}
创建这样的主题
responseArrived=new Subject();
并在管道后像这样添加 takeuntil
var myObservable = this.queryData(server).pipe(takeUntil(responseArrived),map...
并且在代码行return数据中调用
responseArrived.next()
IMO 最好避免使用 for
循环来订阅多个可观察对象。它可能会导致多个开放订阅。这种情况下使用的常用函数是 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
运算符,其 inclusive
参数设置为 true
(感谢@Chris)以根据条件停止并 return 最后一个值.
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)
);
}
注意:尝试调整 takeWhile
谓词中的条件以满足您的要求。
编辑 1:在 takeWhile
opeartor
inclusive
参数
编辑 2:在 filter
运算符中添加附加条件
如果您的唯一条件是在至少收到一个响应后取消请求,难道不能简单地取消订阅从 HttpClient
调用返回的可观察对象吗?
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;
})
);
}