#angular #rxjs
#angular #rxjs
Вопрос:
У меня есть фрагмент кода, который выглядит следующим образом:
getPersons().subscribe(
persons => {
for (const person of persons) {
getAddress(person.id).subscribe(
address => {
person.address = address;
}
);
}
doSomethingWithAddresses();
}
);
Проблема в том, что doSomethingWithAddresses выполняется до завершения всех наблюдаемых getAddress. Как вы можете убедиться, что все они завершены перед выполнением последующего кода?
Ответ №1:
Вы должны использовать forkJoin RxJS, чтобы дождаться завершения цикла for..of, прежде чем возвращать все наблюдаемые.
Вот изменения, которые вы должны внести в свой код:
getPersons().subscribe(
persons => {
const observablesList = [];
for (const person of persons) {
const getAddressObservable = getAddress(person.id);
observablesList.push(getAddressObservable)
}
forkJoin(observablesList).subscribe(response => {
// console.log(response) to check that there is a list of returned observables
const result = persons.map((person, index) => {
person['address'] = response[index]['address'];
return person;
})
doSomethingWithAddresses();
})
}
);
В качестве альтернативы, вы можете попробовать это, чтобы предотвратить цепочку subscribe()
getPersons().pipe(
mergeMap(persons => {
const observablesList = [];
for (const person of persons) {
const getAddressObservable = getAddress(person.id);
observablesList.push(getAddressObservable)
}
return observablesList;
})
).subscribe(response => {
// console.log(response) to check that there is a list of returned observables
const result = persons.map((person, index) => {
person['address'] = response[index]['address'];
return person;
})
doSomethingWithAddresses();
})
Комментарии:
1. Спасибо, это выглядит как правильный подход, но мне все равно нужно назначить каждый адрес каждому пользователю перед вызовом doSomethingWithAddresses() . Возможно ли это с помощью forkJoin?
2. @Spacejockey На самом деле, да. Когда вы подписываетесь на наблюдаемые из forkJoin, вы поймете, что он возвращает список возвращаемых значений из наблюдаемых. В псевдокоде вы получите что-то вроде этого
[addressObj1, addressObj2, addressObj3, etc, etc]
. Оттуда вы, вероятно, можете использовать Array.map() для сопоставления каждого элемента в массиве, чтобы назначить адрес каждому пользователю! Позвольте мне попытаться добавить это к моему ответу, чтобы он был более четко проиллюстрирован.3. Дайте мне знать, если это сработает, я добавлю еще одно альтернативное решение, чтобы сделать это более чистым, поскольку я понимаю, что я здесь размещаю подписки.
4. @wentjun о, ты меня неправильно понял, приятель. на самом деле я хотел учиться. В каком-то месте я видел, что некоторые используют mergemap для подобных сценариев. но здесь вы создаете список наблюдаемых. Я попытался понять решение и задал этот вопрос
5. @SadidKhan Извините за это, я не хотел показаться резким. TBH, я думаю, есть несколько способов подойти к этому, но моя идея состоит в том, чтобы обрабатывать это «по частям» и разделять проблемы. Я подумал, что было бы неплохо, если бы мы дождались завершения наблюдаемых в цикле for..of, прежде чем переходить к следующему набору операций. Поэтому я решаю объединить их и вернуть наблюдаемые, когда циклы for..of будут полностью выполнены ..! Это своего рода «надежный» способ гарантировать, что
doSomethingWithAddresses()
он будет выполняться только после завершения циклов for.
Ответ №2:
let loadedPerson;
getPersons().pipe(
mergeMap(persons => {
return of(persons);
}),
mergeMap(person => {
loadedPerson = person;
return getAddresses(person.id);
}),
map((address) => {
loadedPerson.address = address;
}),
tap(()=>{
doSomethingWithAddresses();
})
).subscribe();
Ответ №3:
Попробуйте этот подход
methodOne() {
getPersons().subscribe(
persons => {
for (const person of persons) {
getAddress(person.id).subscribe(
address => {
person.address = address;
}
);
}
}
);
}
async methodTwo() {
await methodOne();
doSomethingWithAddresses();
}
Комментарии:
1. Поскольку
methodOne()
isn не отслеживает свои дочерние подписки или ничего не возвращает, неdoSomethingWithAddresses()
будет ли запускаться, не дожидаясь их завершения?