我正在尝试处理HTTP调用的订阅嵌套在迭代中的情况。
1)在第一个返回的observable中,我将获得一个URLS对象
2)我需要遍历这些URL并在这些URls上进行http调用
3)在第三步中,同样的情况,我将获得URL并执行与第二步相同的操作
4)最后,我需要将第三步获得的数据推送到最终对象。
let finaldata = [];
this.service1.getstep1Data().subscribe((data1) => {
// data1 = { name : 'abc', urls : ['http://url1.com','http://url2.com',........,'http://urlz.com']};
data1.urls.foreach((url) => {
this.service2.getstep2Data(url).subscribe((data2) => {
// data2 = { name : 'abc', urls : ['http://url1.com','http://url2.com',........,'http://urlz.com']};
data2.urls.foreach((url) => {
this.service3.getfinalData(url).subscribe((data) => {
// data = ["one","two"...."xyz"]
finaldata.push(data[0]);
})
})
})
})
})
现在的问题是在迭代中处理异步调用,这种调用不会彼此等待。
我想等待所有异步调用在第二步中完成,然后执行第三步,否则第三步我们没有URL来进行剩余调用
我知道迭代异步调用不是一个好习惯。
有人可以帮我实现最佳实践吗?
************预先感谢**************
答案 0 :(得分:1)
您可以使用forkJoin
执行多个http请求,然后等待所有请求完成。然后归结为以正确的方式映射到合并的结果,然后提取所需的数据。
import { of, Observable, forkJoin } from 'rxjs';
import { switchMap, concatMap, map, reduce } from 'rxjs/operators';
this.service1.getStep1Data()
.pipe(
// we execute multiple getSet2Data requests for each data1 and wait for each to complete
switchMap(data1 => forkJoin(data1.urls.map(url => this.service2.getStep2Data(url)))),
// we spread the data2 responses
switchMap(data2s => of(...data2s)),
// we execute multiple getfinalData requests for each data2 and emit the results in the
// order of the data2 results
concatMap(data2 => forkJoin(data2.urls.map(url => this.service3.getfinalData(url)))),
// we map to the data we want from the finalData result
map(data3perData2 => data3perData2.map(data3 => data3[0])),
// we concatenate the results so that only one array gets emmited
reduce((acc, data) => acc.concat(data))
)
.subscribe(finalData => this.doMyThing(finalData));
或者,您可以先将多个forkJoin
的结果与另一个forkJoin
合并,而不是先传播data2响应并减少后来的Observable。
this.service1.getStep1Data()
.pipe(
switchMap(data1 => forkJoin(data1.urls.map(url => this.service2.getStep2Data(url)))),
// execute getfinalData for every url from data2 and wait for all results
// do this for every data2 object
switchMap(data2s => forkJoin(data2s.map(data2 => forkJoin(data2.urls.map(url => this.service3.getfinalData(url)))))),
// fullData will be string[][][], so we flatten that to string[] with the first elements
// from data3
map(fullData => [].concat(...fullData).map(data3 => data3[0]))
)
.subscribe(finalData => this.doMyThing(finalData));
最后的映射和归约取决于您希望最终输出的样子。
答案 1 :(得分:0)
要对服务器RxJs运算符(例如mergeMap)进行多个HTTP调用,forkJoin将是处理这种情况的最佳方法。
下面的示例示例将对您有所帮助。
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { forkJoin } from "rxjs/observable/forkJoin";
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
loadedCharacter: {};
constructor(private http: HttpClient) { }
ngOnInit() {
let character = this.http.get('https://swapi.co/api/people/1');
let characterHomeworld = this.http.get('http://swapi.co/api/planets/1');
forkJoin([character, characterHomeworld]).subscribe(results => {
// results[0] is our character
// results[1] is our character homeworld
results[0].homeworld = results[1];
this.loadedCharacter = results[0];
});
}
}
答案 2 :(得分:0)
您可以使用RXJS forkjoin实现解决方案。
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular';
constructor(private appservice: AppService){
this.appservice.getService1().subscribe(urls=>{
this.appservice.getService2(urls).subscribe(finalArrayList=>{
this.appservice.getService3(finalArrayList).subscribe();
});
});
}
ngOnInit(){
}
}
创建了Stackblitz https://stackblitz.com/edit/angular-fkh7xr
答案 3 :(得分:0)
您可以使用Subject代替简单的观察者。主题将提供时间间隔更新,因此将有助于同步使用api数据。
this.api.subjectCreateUser1.subscribe((data1) => {
if (data1) {
this.api.subjectCreateUser2.subscribe((data2) => {
if(data2) {
this.api.subjectCreateUser3.subscribe((data3) => {
if (data3) {
console.log(data3);
}
});
}
});
}
});
和如下所示的api调用。...
return this.newHttp.post(this._baseApiUrl, this.data1)
.subscribe(success => {
console.log(success);
this.subjectCreateUser1.next(success);
}, error => {
this.subjectCreateUser1.next(error);
});
希望这会有所帮助。