如何在 Angular 中定义订阅 Observables 的执行顺序?
How can I define the execution order of subscription to Observables in Angular?
我从 service.ts 文件中的 API 收到了一些 get-Call:
//code
getCars()
{
this.car = this.http.get(car_url)
return this.car;
}
getTires()
{
this.tires = this.http.get(tires_url)
return this.tires;
}
getSeats()
{
this.seats = this.http.get(seats_url)
return this.seats;
}
在detail.compoenent.ts中,我将这些数据过滤到选定的汽车,然后通过detail.component.html呈现- detail.compoenent.ts 看起来像这样:
//code
ngOnInit()
{
this.Service.getCars()
.subscribe(cars => this.car = cars.find(/*code (selecting car with certain car_id*/)
this.Service.getTires()
.subscribe(tires => {this.tires = tires.filter(/*code (selecting all the tires with the selected car_id*/)}
this.Service.getSeats()
.subscribe(seats => {this.seats = seats.filter(/*code (selecting all the seats with the selected car_id*/)}
}
要过滤轮胎和座椅,必须先执行getCar(),因为需要它的信息来过滤轮胎和座椅。那么我该如何放置代码以确保 this.Service.getCars()subscribe(/code/) 在 this.Service.getTires().subscribe(/code/) 和 this.Service.getSeats().subscribe(/code/)?
你可以使用mergeMap()
然后调用其他的observables,你可以在另一个API中使用另一个之前的数据。
this.Service.getCars().pipe(mergeMap((cars) => {
// cars is available for the other two
return this.Service.getTires().pipe(mergeMap((tierData) => {
// tierData is avaibale for getting seats.
return this.Service.getSeats()
}))
})).subscribe((seats) => {
})
您可以将其他两个合并到第一个管道中。
简单的解决方案可以是在回调函数中进行后续的 http 请求。
ngOnInit()
{
this.Service.getCars().subscribe(
cars => {
this.car = cars.find();
this.Service.getTires().subscribe(tires => {
this.tires = tires.filter();
this.Service.getSeats().subscribe(
seats => {this.seats =
seats.filter(/*code (selecting all
the seats with the selected car_id*/)}
}
}
});
}
在加载所有数据之前,您可以向最终用户显示微调器。
嵌套订阅是一种很大的代码味道。如果您觉得需要嵌套订阅,请搜索更高阶的可观察对象,如 switchMap、concatMap、mergeMap、zip 等。
我花时间构建了您要实现的目标的完整演示。
首先,让我们开始定义我们的接口以具有某种类型安全性:
export interface Car {
id: string;
seatId: string;
tyreId: string;
}
export type CarResolved = Omit<Car, "seatId" | "tyreId"> & {
seat: Seat;
tyre: Tyre;
};
export interface Tyre {
id: string;
diameter: number;
}
export interface Seat {
id: string;
width: number;
}
现在我们知道我们想要什么样的数据结构,让我们构建一个服务和 return 模拟数据,您稍后可以用您的后端替换这些数据:
@Injectable()
export class ResourcesService {
public getCars(): Observable<CarResolved[]> {
return this._getCars().pipe(
switchMap(cars =>
forkJoin(
...cars.map(car =>
forkJoin(
this._getTyreById(car.tyreId),
this._getSeatById(car.seatId)
).pipe(
map(([tyre, seat]) => ({
id: car.id,
seat,
tyre
}))
)
)
)
)
);
}
private _getCars(): Observable<Car[]> {
return of(mockCars);
}
private _getTyreById(id: string): Observable<Tyre> {
return of(mockTyres.find(tyre => tyre.id === id));
}
private _getSeatById(id: string): Observable<Seat> {
return of(mockSeats.find(seat => seat.id === id));
}
}
const mockCars: Car[] = [
{ id: "car-1", seatId: "seat-1", tyreId: "tyre-1" },
{ id: "car-2", seatId: "seat-2", tyreId: "tyre-2" },
{ id: "car-3", seatId: "seat-1", tyreId: "tyre-3" }
];
const mockTyres: Tyre[] = [
{ id: "tyre-1", diameter: 80 },
{ id: "tyre-2", diameter: 60 },
{ id: "tyre-3", diameter: 75 }
];
const mockSeats: Seat[] = [
{ id: "seat-1", width: 10 },
{ id: "seat-2", width: 20 },
{ id: "seat-3", width: 30 }
];
如果您查看 getCars
方法,它绝对没有订阅。它只是 return 一个有人可以稍后订阅的可观察对象。一切都通过流完成:)
最后,最简单的部分:视图。
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
public cars$: Observable<CarResolved[]> = this.resourcesService.getCars();
constructor(private resourcesService: ResourcesService) {}
}
这里也请注意:完全没有订阅! :)
HTML 方:
<pre>{{ cars$ | async | json }}</pre>
我们的视图现在显示已解决的汽车数组:
[
{
"id": "car-1",
"seat": {
"id": "seat-1",
"width": 10
},
"tyre": {
"id": "tyre-1",
"diameter": 80
}
},
{
"id": "car-2",
"seat": {
"id": "seat-2",
"width": 20
},
"tyre": {
"id": "tyre-2",
"diameter": 60
}
},
{
"id": "car-3",
"seat": {
"id": "seat-1",
"width": 10
},
"tyre": {
"id": "tyre-3",
"diameter": 75
}
}
]
这是 stackblitz 上的现场演示:https://stackblitz.com/edit/angular-qypary
我从 service.ts 文件中的 API 收到了一些 get-Call:
//code
getCars()
{
this.car = this.http.get(car_url)
return this.car;
}
getTires()
{
this.tires = this.http.get(tires_url)
return this.tires;
}
getSeats()
{
this.seats = this.http.get(seats_url)
return this.seats;
}
在detail.compoenent.ts中,我将这些数据过滤到选定的汽车,然后通过detail.component.html呈现- detail.compoenent.ts 看起来像这样:
//code
ngOnInit()
{
this.Service.getCars()
.subscribe(cars => this.car = cars.find(/*code (selecting car with certain car_id*/)
this.Service.getTires()
.subscribe(tires => {this.tires = tires.filter(/*code (selecting all the tires with the selected car_id*/)}
this.Service.getSeats()
.subscribe(seats => {this.seats = seats.filter(/*code (selecting all the seats with the selected car_id*/)}
}
要过滤轮胎和座椅,必须先执行getCar(),因为需要它的信息来过滤轮胎和座椅。那么我该如何放置代码以确保 this.Service.getCars()subscribe(/code/) 在 this.Service.getTires().subscribe(/code/) 和 this.Service.getSeats().subscribe(/code/)?
你可以使用mergeMap()
然后调用其他的observables,你可以在另一个API中使用另一个之前的数据。
this.Service.getCars().pipe(mergeMap((cars) => {
// cars is available for the other two
return this.Service.getTires().pipe(mergeMap((tierData) => {
// tierData is avaibale for getting seats.
return this.Service.getSeats()
}))
})).subscribe((seats) => {
})
您可以将其他两个合并到第一个管道中。
简单的解决方案可以是在回调函数中进行后续的 http 请求。
ngOnInit()
{
this.Service.getCars().subscribe(
cars => {
this.car = cars.find();
this.Service.getTires().subscribe(tires => {
this.tires = tires.filter();
this.Service.getSeats().subscribe(
seats => {this.seats =
seats.filter(/*code (selecting all
the seats with the selected car_id*/)}
}
}
});
}
在加载所有数据之前,您可以向最终用户显示微调器。
嵌套订阅是一种很大的代码味道。如果您觉得需要嵌套订阅,请搜索更高阶的可观察对象,如 switchMap、concatMap、mergeMap、zip 等。
我花时间构建了您要实现的目标的完整演示。
首先,让我们开始定义我们的接口以具有某种类型安全性:
export interface Car {
id: string;
seatId: string;
tyreId: string;
}
export type CarResolved = Omit<Car, "seatId" | "tyreId"> & {
seat: Seat;
tyre: Tyre;
};
export interface Tyre {
id: string;
diameter: number;
}
export interface Seat {
id: string;
width: number;
}
现在我们知道我们想要什么样的数据结构,让我们构建一个服务和 return 模拟数据,您稍后可以用您的后端替换这些数据:
@Injectable()
export class ResourcesService {
public getCars(): Observable<CarResolved[]> {
return this._getCars().pipe(
switchMap(cars =>
forkJoin(
...cars.map(car =>
forkJoin(
this._getTyreById(car.tyreId),
this._getSeatById(car.seatId)
).pipe(
map(([tyre, seat]) => ({
id: car.id,
seat,
tyre
}))
)
)
)
)
);
}
private _getCars(): Observable<Car[]> {
return of(mockCars);
}
private _getTyreById(id: string): Observable<Tyre> {
return of(mockTyres.find(tyre => tyre.id === id));
}
private _getSeatById(id: string): Observable<Seat> {
return of(mockSeats.find(seat => seat.id === id));
}
}
const mockCars: Car[] = [
{ id: "car-1", seatId: "seat-1", tyreId: "tyre-1" },
{ id: "car-2", seatId: "seat-2", tyreId: "tyre-2" },
{ id: "car-3", seatId: "seat-1", tyreId: "tyre-3" }
];
const mockTyres: Tyre[] = [
{ id: "tyre-1", diameter: 80 },
{ id: "tyre-2", diameter: 60 },
{ id: "tyre-3", diameter: 75 }
];
const mockSeats: Seat[] = [
{ id: "seat-1", width: 10 },
{ id: "seat-2", width: 20 },
{ id: "seat-3", width: 30 }
];
如果您查看 getCars
方法,它绝对没有订阅。它只是 return 一个有人可以稍后订阅的可观察对象。一切都通过流完成:)
最后,最简单的部分:视图。
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
public cars$: Observable<CarResolved[]> = this.resourcesService.getCars();
constructor(private resourcesService: ResourcesService) {}
}
这里也请注意:完全没有订阅! :)
HTML 方:
<pre>{{ cars$ | async | json }}</pre>
我们的视图现在显示已解决的汽车数组:
[
{
"id": "car-1",
"seat": {
"id": "seat-1",
"width": 10
},
"tyre": {
"id": "tyre-1",
"diameter": 80
}
},
{
"id": "car-2",
"seat": {
"id": "seat-2",
"width": 20
},
"tyre": {
"id": "tyre-2",
"diameter": 60
}
},
{
"id": "car-3",
"seat": {
"id": "seat-1",
"width": 10
},
"tyre": {
"id": "tyre-3",
"diameter": 75
}
}
]
这是 stackblitz 上的现场演示:https://stackblitz.com/edit/angular-qypary