我有一个街道列表,并对它们进行迭代。在for循环中,我将为每条街道调用一个端点。端点会向我提供有关所请求街道的信息。我想把每个响应存储到一个对象数组中,在所有请求完成后,我想执行下一个代码块。这是我的问题:我执行所有调用,将所有数据存储到对象数组中,但如果我要在下一个代码块中使用我的预填充对象数组,则长度=0…这里是我的代码:
export class MyComponent{
addressInfoArray: AddressInfo[] = [];
// some code ...
prepareStreetInformations(): void {
// some code ....
this.fillArray(streets, url);
this.doSomethingWithArray(this.addressInfoArray); // <--- length = 0 and doesn't waits for finishing the fillArray() method
}
}
fillArray(streets: Street[], url: string): void { // streets has a length of 150
for (const street of streets) {
this.http.get<AddressInfo>(`${url}/street.name`).subscribe(response => {
this.addressInfoArray.push(response);
});
}
}
所以我的问题是:如何等待doSomethingWithArray((方法完全完成fillArray((方法,为什么doSometingWithArray(方法看不到我的Object Array已经填充好了?
使用RxJS,您不会真正试图强制函数调用;等待";对于先前的功能;相反,您可以构建一个可观测对象,该可观测对象根据其他可观测对象发出您需要的数据。
在您的情况下,看起来您需要一个发射AddressInfo
数组的可观察对象。
我们可以定义返回Observable<Street[]>
的getStreets()
方法,也可以定义接受street
参数并返回Observable<AddressInfo>
的getAddressInfo()
方法。
现在,我们可以使用switchMap
和forkJoin
:创建一个将发射AddressInfo[]
的可观察对象
1 export class MyComponent {
2
3 getStreets(): Observable<Street[]> {
4 this.http.get<Street[]>(...);
5 }
6
7 getAddressInfo(street: Street): Observable<AddressInfo> {
8 this.http.get<AddressInfo>(`${this.url}/${street.name}`); // don't subscribe here
9 }
10
11 addressInfos$: Observable<AddressInfo[]> = this.getStreets().pipe(
12 switchMap(streets => forkJoin(
13 streets.map(s => this.getAddressInfo(s))
14 ))
15 );
16
17 }
我们使用forkJoin
来创建一个可观测值,该可观测值发出其所有输入可观测值的结果数组。因此,我们将一组可观测值作为输入传递给它。
因为你有一排街道。在#13行,我们简单地将它映射到一个可观察的数组,该数组可以获得地址信息。现在,当订阅forkJoined observable时,它将发出一个AddressInfo
数组(所有单个http调用的结果(
我们使用CCD_;分叉可观察";结果是可观察到的单个addressInfos$
发射AddressInfo[]
。
请注意,我们尚未订阅。要做你的工作,你可以简单地订阅:
addressInfos$.subscibe(
infos => doSomethingWithArray(infos)
);
然而,从角度来看,一种典型的方法是将数据的发射进一步转换为模板所需的形状:
templateData$ = this.addressInfos$.pipe(
map(infos => {
// do something will array :-)
})
);
然后,在您的模板中,您可以利用async
管道:
<div *ngIf="templateData$ | async as data">
<ul>
<li *ngFor="item of data">{{ item }}</li>
</ul>
</div>
尝试使用forkJoin;
const streetsObservs = this.streets.map(street => this.http.get<AddressInfo>(`${url}/street.name`));
forkJoin(streetsObservs).subscribe((response: any) => {
this.addressInfoArray = response; // this is an array
this.doSomethingWithArray(this.addressInfoArray); // <--- length = 0 and doesn't waits for finishing the fillArray() method
});
我认为您对Observables的工作方式有一个错误的想法。在您的示例代码中,执行fillArray
只是分派http调用,然后函数结束。代码中的下一步是调用doSomethingWithArray
,当然此时addressInfoArray
还没有填充。然后,一段时间后,当http调用完成时,将执行您在subscribe
中提供的回调函数,调用addressInfoArray.push
。当使用subscribe
时,情况总是如此。当Observable完成时,它只会执行您传递给它的函数。
如果您想等待许多Observable完成,可以使用combineLatest
。这创建了一个Observable,它发出一个包含原始Observable中所有值的数组。这就是它的工作原理:
prepareStreetInformations(): void {
this.fillArray(streets, url).subscribe(addressInfoArray => {
this.doSomethingWithArray(this.addressInfoArray)
});
}
fillArray(streets: Street[], url: string): Observable<AddressInfo[]> {
return combineLatest(
streets.map(street => this.http.get<AddressInfo>(`url/${street.name}`))
);
}
请注意,fillArray
现在返回一个可订阅的Observable,并且不再对类属性进行任何更改。如果需要类属性,则必须在传递给subscribe
的回调函数中定义它。