Я работаю над личным проектом, и у меня есть служба, которая возвращает массив изображений из хранилища firebases. Проблема в том, что когда я подписываюсь на наблюдаемое, я получаю пустой массив. Когда я открываю массив, все данные есть. Проблема в том, что когда я пытаюсь что-то сделать с массивом, я получаю значение undefined (например, console.info(myArray[0]) is undefined). Если я прав, проблема в том, что наблюдаемые асинхронны, и поэтому я получаю пустой массив. Мой вопрос, как я могу решить эту проблему?
Вот служба
getImagesForChosenRestaurant(id: string): Observable<any> {
this.imageList = []
this.imageRef = ref(storage, `food/${id}`)
listAll(this.imageRef).then(res => {
res.items.forEach(item => {
getDownloadURL(item).then(url => {
this.imageList.push(url);
})
})
})
return of(this.imageList);
}
А вот подписка
ngOnInit(): void {
this.route.params.subscribe(params => {
this.restaurantService.getImagesForChosenRestaurant(params['id']).subscribe((foodPic: any) => {
this.foodImages = foodPic;
})
})
}
Спасибо за любой ответ.
И как я могу предотвратить это?
Зависит. Является ли getDownloadURL действительно асинхронным?
Это из firebase, поэтому я не уверен.





попробуй это:
async getImagesForChosenRestaurant(id: string): Promise<void> {
this.imageList = []
this.imageRef = ref(storage, `food/${id}`)
const list = await listAll(this.imageRef);
list.items.forEach(async item => {
const url = await getDownloadURL(item);
this.imageList.push(url);
})
return of(this.imageList);
}
Я сомневаюсь, что это сработает. forEach не «осведомлен» об асинхронности, поэтому ожидание в цикле не даст ожидаемого результата.
хорошо, может быть, вы правы, поэтому я добавляю async в цикл forEach
Я пробовал это, и это все еще не работает
ref() это не асинхронная функция?
forEach не является ожидаемым, поэтому его невозможно использовать с async. Если вы переключите этот код на простой цикл for с await внутри - он будет работать. Но минусами этого решения является его последовательное выполнение, одно за другим. С комбинацией Promise.all() и .map() вы можете запустить их все сразу.
Вы можете использовать метод .map для преобразования массива элементов из listAll в массив обещаний для getDownloadURL. После этого просто используйте Promise.all() с rxjs from.
import { from, Observable } from "rxjs";
import { ref, listAll, getDownloadURL } from "firebase/storage";
getImagesForChosenRestaurant(id: string): Observable<string[]> {
const wrapperFn = async () => {
const imageRef = ref(storage, `food/${id}`);
const listAllResult = await listAll(imageRef);
const imagesDownloadUrlsPromises = listAllResult.items.map((item) =>
getDownloadURL(item)
);
return Promise.all(imagesDownloadUrlsPromises);
};
return from(wrapperFn());
}
Этот работает. Спасибо.
Попробуй это
getImagesForChosenRestaurant(id: string): Observable<string[]> {
const imageRef = ref(storage, `food/${id}`);
return from(listAll(imageRef)).pipe(
switchMap((res) => forkJoin(res.items.map(getDownloadURL)))
);
}
возврат (this.imageList); Будет выполняться перед любыми нажатиями на список