I wrote code that retrieves urls of images stored in my Firebase Storage bucket and loads them in an imageUrls
array.
The code works in about 80% of instances, but for the remaining (seemingly random 20%), when the code is run, it fails to resolve some promises, but doesn't throw an exception either.
Code:
async function loadImages() {
// Load image urls from firebase storage
Object.values(imageOrder).forEach(async (fileName, index) => { // Iterate through each imageName for image that should be loaded (e.g., "dogs.jpg", "mountains.jpeg")
const imgRef = ref(storage, `images/projects/${project.id}/draft/${fileName}`)
const imgUrl = await getDownloadURL(imgRef) // this promise may never get resolved for some iterations of the loop
const imgPos = index // store index in array at which this image should be inserted into
setImageUrls(prevState => {
let newArray = [...prevState]
newArray = newArray.map((item, index) => index === imgPos ? imgUrl : item) // replace null value in array with loaded image url at appropriate index
return newArray
})
})
}
The promise that never gets resolved appears above in the line const imgUrl = await getDownloadURL(imgRef)
.
I inserted console log statements at various places to understand the details of what is happening. All image names I expected are iterated through in the forEach
loop, and an imgRef
reference is created for each.
In some cases though, only a subset of the promises get resolved. In the rest, the code after the await
statement does not get called, and therefore the resulting imageUrls
array contains a combination of url strings as well as null
values.
Why may this be happening? Is there anything else I can do to clarify the problem further? Thanks.
CodePudding user response:
You can try running those getDownloadURL()
promises at once using Promise.all()
. Try refactoring the code as shown below:
async function loadImages() {
// Load image urls from firebase storage
const promises = [];
Object.values(imageOrder).forEach(image => {
const imgRef = ref(storage, `images/projects/${project.id}/draft/${fileName}`);
promises.push(getDownloadURL(imgRef));
})
const urls = (await Promise.all(promises)).filter(url => !!url);
setImageUrls(urls);
}
Returned values from
Promise.all()
will be in order of the Promises passed, regardless of completion order.
So you shouldn't need to explicitly sort using index
. Also the filter()
at end removes any null
values.
Also checkout: Using async/await with a forEach loop