Angular 7 个嵌套的可观察对象
Angular 7 nested observables
我有两个collections:人和宠物。每只宠物都有 personId。我的目标是让所有人和每个人在单个 json 中添加 his/her 宠物。到目前为止我所做的是:
this.personService.getPersions().subscribe(persons => {
const personsWithPets = persons.flatMap(person => this.petService.getPetsByPersonId(person._id)
.subscribe(petsData => {
person.pets = petsData;
return person;
}, (err) => {
console.log(err);
}));
this.persons = personsWithPets; // This is fired before previous subscribe
}, (err) => {
console.log(err);
});
我做错了什么?为什么 this.persons = personsWithPets;
在订阅完成前被触发?
在 subscribe 中使用 subscribe 被认为是一种不好的做法,可能会导致您描述的问题。我建议将 mergeMap 与 forkJoin 运算符结合使用:
this.personService.getPersions().pipe(mergeMap(persons => {
const requests = persons.map(person => this.petService.getPetsByPersonId(person._id));
return forkJoin(of(persons), ...requests);
}),
map(values => {
const persons = values[0];
const pets = values.slice(1);
// here you need to assign correct pet to correct person
})
).subscribe(personsWithPets => {
console.log(personsWithPets);
}, err => {
console.log(err);
});
我给你做了个例子
const { of } = rxjs;
const { map, switchMap, toArray, mergeMap } = rxjs.operators;
function getPeople() {
return of([{ id: 1, name: 'Amanda' }, { id: 2, name: 'Nancy' }]);
}
function getPetsByPersonId(id) {
switch(id) {
case 1:
return of(['Doggie']);
case 2:
return of(['Kitten']);
}
}
const getPets = (person) => {
return getPetsByPersonId(person.id).pipe(
map(pets => ({ ...person, pets }))
)
}
getPeople().pipe(
switchMap(people => people),
mergeMap(getPets),
toArray()
)
.subscribe(peopleWithPets => console.log(peopleWithPets));
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
Observable 本质上是异步的。订阅调用将在另一个线程中 运行,而方法中的其余指令将在主线程中继续 运行。您需要做的是等到异步操作完成后再 运行 执行下一个操作。您可以通过将 isintructions 放入订阅中来完成此操作,或者更好地使用 (err) 参数之后的 onCompleted 参数,例如
this.personService.getPersons().subscribe(persons => {
const personsWithPets = persons.flatMap(person =>
this.petService.getPetsByPersonId(person._id)
.subscribe(petsData => {
person.pets = petsData;
return person;
},
(err) => {
console.log(err);
},
() => {
this.persons = personsWithPets;
}))
});
已更新 添加评论
另一个:stackblitz
this.service.getPersons().pipe(switchMap((per:any[])=>{
//create an array of observables
const obs=per.map(per=>this.service.getPet(per.id));
//call all of them in forkjoin
return forkJoin(obs).pipe(map(pets=>
//pets is an array, in pets[0] is the response of getPet(1),
//in pets[1] is the response of getPet(2)
pets.map((pet,i)=>{
return {
...per[i], //all the properties of the person
pets:pet //+ in pets an array with the pets of the person
}
})
))
})).subscribe(res=>this.res=res)
我有两个collections:人和宠物。每只宠物都有 personId。我的目标是让所有人和每个人在单个 json 中添加 his/her 宠物。到目前为止我所做的是:
this.personService.getPersions().subscribe(persons => {
const personsWithPets = persons.flatMap(person => this.petService.getPetsByPersonId(person._id)
.subscribe(petsData => {
person.pets = petsData;
return person;
}, (err) => {
console.log(err);
}));
this.persons = personsWithPets; // This is fired before previous subscribe
}, (err) => {
console.log(err);
});
我做错了什么?为什么 this.persons = personsWithPets;
在订阅完成前被触发?
在 subscribe 中使用 subscribe 被认为是一种不好的做法,可能会导致您描述的问题。我建议将 mergeMap 与 forkJoin 运算符结合使用:
this.personService.getPersions().pipe(mergeMap(persons => {
const requests = persons.map(person => this.petService.getPetsByPersonId(person._id));
return forkJoin(of(persons), ...requests);
}),
map(values => {
const persons = values[0];
const pets = values.slice(1);
// here you need to assign correct pet to correct person
})
).subscribe(personsWithPets => {
console.log(personsWithPets);
}, err => {
console.log(err);
});
我给你做了个例子
const { of } = rxjs;
const { map, switchMap, toArray, mergeMap } = rxjs.operators;
function getPeople() {
return of([{ id: 1, name: 'Amanda' }, { id: 2, name: 'Nancy' }]);
}
function getPetsByPersonId(id) {
switch(id) {
case 1:
return of(['Doggie']);
case 2:
return of(['Kitten']);
}
}
const getPets = (person) => {
return getPetsByPersonId(person.id).pipe(
map(pets => ({ ...person, pets }))
)
}
getPeople().pipe(
switchMap(people => people),
mergeMap(getPets),
toArray()
)
.subscribe(peopleWithPets => console.log(peopleWithPets));
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
Observable 本质上是异步的。订阅调用将在另一个线程中 运行,而方法中的其余指令将在主线程中继续 运行。您需要做的是等到异步操作完成后再 运行 执行下一个操作。您可以通过将 isintructions 放入订阅中来完成此操作,或者更好地使用 (err) 参数之后的 onCompleted 参数,例如
this.personService.getPersons().subscribe(persons => {
const personsWithPets = persons.flatMap(person =>
this.petService.getPetsByPersonId(person._id)
.subscribe(petsData => {
person.pets = petsData;
return person;
},
(err) => {
console.log(err);
},
() => {
this.persons = personsWithPets;
}))
});
已更新 添加评论
另一个:stackblitz
this.service.getPersons().pipe(switchMap((per:any[])=>{
//create an array of observables
const obs=per.map(per=>this.service.getPet(per.id));
//call all of them in forkjoin
return forkJoin(obs).pipe(map(pets=>
//pets is an array, in pets[0] is the response of getPet(1),
//in pets[1] is the response of getPet(2)
pets.map((pet,i)=>{
return {
...per[i], //all the properties of the person
pets:pet //+ in pets an array with the pets of the person
}
})
))
})).subscribe(res=>this.res=res)