I am getting url from firebase using ".getDownloadUrl" and then push into an array but array shown empty but inside data available in console
I am using the following code:
const getPostUrl = async (item) => {
const url = await storage().ref(item.data?.postName).getDownloadUrl();
return { ...item._data, URL: url };
};
const getUrls = async () => {
let response=[]
storage().list((data)=>console.log(data, "knok knok tera baaap aaya"))
PostCollection.doc(UserInfo.uid)
.collection("post")
.onSnapshot(documentSnapshot => {
documentSnapshot.docs.forEach ( async (item, i) => {
const getdata = await getPostUrl(item)
response.push(getdata)
// const url = await storage().ref(item._data?.postName).getDownloadUrl()
// const obj = { ...item._data, URL:res }
});
// setPosts(postArr);
// setUrls(newArr);
});
let xyz = await Promise.resolve(response)
console.log(response, xyz, "RESPONSE========>");
};
Note: Originally code provided as image: here
CodePudding user response:
Diagnosis
Your code is executed asyncronously. In simple terms that means that conceptually there is no guarantee of execution order among code portions flagged as async
and wrt to the (synchronous) rest of the code.
Specifically that applies to the handler passed as argument to .forEach
where the array is filled and the console output statement.
What likely happens is that the array is written to the console before the code to fill it with downloaded information has executed.
Remedy
Add additional synchronization to guarantee that the array is filled before using/outputting it.
Implementation
Instead of await
-ing the result of async execution in various places of the code, pass the abstraction of the future results as Promises. In particular, collect the responses as an array of Promises instead of an array of results. If all promises are resolved (in production code you would prefer to have them all 'settled', as some may fail), continue processing, eg. output to console.
Pitfalls
Promise.all
(For the in-browser implementation, see here (MDN), available in most Promise libraries) processes the argument array as is without regard to whether it contains the proper number of arguments. This is no problem here as the array-filling forEach
loop is executed synchronously but must be looked after when modifying the code.
Beware: The following code is untested !
const getPostUrl = (item) => {
const p_url =
storage().ref(item.data?.postName).getDownloadUrl()
.then ( (url) => { return Promise.resolve({ ...item._data, URL: url }, (e) => { /* flag error */ } )
;
return p_url;
};
const getUrls = async () => {
let p_response=[]
storage().list((data)=>console.log(data, "knok knok tera baaap aaya"))
PostCollection.doc(UserInfo.uid)
.collection("post")
.onSnapshot(documentSnapshot => {
documentSnapshot.docs.forEach ( (item, i) => {
const getdata = getPostUrl(item)
p_response.push(getdata)
// const url = await storage().ref(item._data?.postName).getDownloadUrl()
// const obj = { ...item._data, URL:res }
});
Promise.all ( p_response )
.then ( (a_results) => {
console.log ( `Response ========> ${JSON.stringify(a_results)}.`)
}, (e) => { /* flag error */ }
);
// setPosts(postArr);
// setUrls(newArr);
});
};
Recommendation
Encapsulate the pattern of collecting an array of a predetermined number of promises and firing when they are all settled into a class of their own (or use a Promise/sync library that comes with it).
CodePudding user response:
Can you share the response of console.log() Please provide sufficient data to have a better understanding of the issue.