admin管理员组文章数量:1433496
I have two collections: persons and pets. Every pet has personId. My target is to get all persons and foreach of them to add his/her pets in single json. What I did so far is:
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);
});
What I do wrong? Why this.persons = personsWithPets;
is fired before subscription finish?
I have two collections: persons and pets. Every pet has personId. My target is to get all persons and foreach of them to add his/her pets in single json. What I did so far is:
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);
});
What I do wrong? Why this.persons = personsWithPets;
is fired before subscription finish?
-
1
because your
getPetsByPersonId
is probably asynchronus, so the code inside your call togetPetsByPersonId().subscribe
will likely not return a value until afterthis.persons = personsWithPets
. Thesubscribe
is called, just the code inside won't run until the service returns a value. It really has nothing to do with nested observables – BlackICE Commented Mar 13, 2019 at 11:08 - Any suggestions to make it works? – IntoTheDeep Commented Mar 13, 2019 at 11:13
4 Answers
Reset to default 2Updated added ments
Another one: 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)
I have made an example for you
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./rxjs/bundles/rxjs.umd.min.js"></script>
Using subscribe inside subscribe is considered as a bad practice and may lead to problems as you described. I would suggest to use mergeMap bined with forkJoin operator:
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);
});
Observables are asynchronous by nature. The subscribe call will be run in another thread while the rest of the instructions in the method will continue to run in the main thread. What you need to do is wait until the asynchronous action pletes before running the next one. You can do this by putting the isntructions inside of the subscription, or better yet use the onCompleted parameter which es after the (err) param, like so
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;
}))
});
本文标签: javascriptAngular 7 nested observablesStack Overflow
版权声明:本文标题:javascript - Angular 7 nested observables - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745618910a2666566.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论