I have the following code to retrieve some file information from Firebase:
function loadUserProfileKeys(key) {
// Get the Firebase storage ref for the InitialUserProfiles folder
var storageRef = firebase.storage().ref();
var initialUserProfilesRef = storageRef.child('InitialUserProfiles'); // .txt files folder
// Array
const keyResults = [];
// Retrieve all profiles
initialUserProfilesRef.listAll()
.then(function(res) {
// Loop over each item
for (const itemRef of res.items) {
console.log("Start for loop");
// Ignore profiles with symbols (workaround - TODO: fix this)
if (/,|&/.test(itemRef.name)) {} else {
// Path of the file
var pathRef = initialUserProfilesRef.child(itemRef.name);
// Get the file's download URL
var downloadURL = pathRef.getDownloadURL()
.then((url) => {
// Get the given key from the user profile text file
getValueKey(url, key)
.then((value) => {
// Add it to the keyResults array
keyResults.push(value);
});
});
}
}
console.log("End for loop");
console.log(keyResults);
}).catch((error) => {
console.log("ERROR");
console.log(error);
});
}
async function getValueKey(fileURL, key) {
let response = await fetch(fileURL);
if (response.status == 200) {
let json = await response.text(); // (3)
var lines = json.split("\n");
var results = [];
for (var i = 0; i < lines.length; i ) {
var line = lines[i];
var pairs = line.split(":");
if (pairs.length == 2 && pairs[0].trim() == key) {
results.push(pairs[1].trim());
}
}
return Promise.resolve(results[0]);
}
}
The logging itself is fine - it won't log "End for loop" until all of the looping, i.e. multiple "Start for loop" logs, is done.
The issue is that this is still before keyResults.push(value);
is called - and thus the array is empty (or very occasionally only partly populated).
How do I get var downloadURL = pathRef.getDownloadURL()
and getValueKey(url, key)
to be blocking - so that it does not iterate over the loop until the nested .then((value)
is called to add to the array?
I can't figure out async etc - I keep getting syntax errors.
CodePudding user response:
If you want to wait for multiple asynchronous operations to all be finished, you'll want to use Promise.all()
.
Something like this should be getting a lot closer:
const keyResults = Promise.all(initialUserProfilesRef.listAll().then(function(res) {
// Loop over each item
return res.items.map((itemRef) => {
// Ignore profiles with symbols (workaround - TODO: fix this)
if (/,|&/.test(itemRef.name)) {} else {
// Path of the file
var pathRef = initialUserProfilesRef.child(itemRef.name);
// Get the file's download URL
return pathRef.getDownloadURL()
.then((url) => {
// Get the given key from the user profile text file
return getValueKey(url, key)
});
}
}
})
console.log("End for loop");
console.log(keyResults);
CodePudding user response:
I would make loadUserProfileKeys
a async function.
That way you can simply await your other async functions (pathRef.getDownloadURL()
, getValueKey(url, key)
).
Here is your Snipped modified using async & await. I did not test it, but it should work.
async function loadUserProfileKeys(key) {
// Get the Firebase storage ref for the InitialUserProfiles folder
var storageRef = firebase.storage().ref();
var initialUserProfilesRef = storageRef.child('InitialUserProfiles'); // .txt files folder
// Array
const keyResults = [];
try {
// Retrieve all profiles
const profileRes = await initialUserProfilesRef.listAll();
// Loop over each item
for (const itemRef of profileRes.items) {
console.log("Start for loop");
// Ignore profiles with symbols (workaround - TODO: fix this)
if (/,|&/.test(itemRef.name)) {} else {
// Path of the file
var pathRef = initialUserProfilesRef.child(itemRef.name);
// Get the file's download URL
var downloadURL = await pathRef.getDownloadURL();
// Get the given key from the user profile text file
keyResults.push(await getValueKey(downloadURL, key));
}
}
console.log("End for loop");
console.log(keyResults);
} catch(error) {
console.log("ERROR");
console.log(error);
}
}
In general i would personally recommend try to avoid encapsulating .then() calls.
It just makes the code harder to read and understand.
I find async & await much cleaner.
CodePudding user response:
You can do like this
async function loadUserProfileKeys(key) {
// Get the Firebase storage ref for the InitialUserProfiles folder
var storageRef = firebase.storage().ref();
var initialUserProfilesRef = storageRef.child('InitialUserProfiles'); // .txt files folder
// Array
const keyResults = [];
// Retrieve all profiles
const res = await initialUserProfilesRef.listAll();
// Loop over each items
res.items.forEach(async (itemRef) => {
console.log("Start for loop");
// Ignore profiles with symbols (workaround - TODO: fix this)
if (/,|&/.test(itemRef.name)) {
// skip
} else {
// Path of the file
const pathRef = initialUserProfilesRef.child(itemRef.name);
// Get the file's download URL
const url = await pathRef.getDownloadURL();
// Get the given key from the user profile text file
const value = await getValueKey(url, key);
.
// Add it to the keyResults array
keyResults.push(value);
}
});
console.log("End for loop");
console.log(keyResults);
}
async function getValueKey(fileURL, key) {
let response = await fetch(fileURL);
if (response.status == 200) {
let json = await response.text(); // (3)
var lines = json.split("\n");
var results = [];
for (var i = 0; i < lines.length; i ) {
var line = lines[i];
var pairs = line.split(":");
if (pairs.length == 2 && pairs[0].trim() == key) {
results.push(pairs[1].trim());
}
}
return Promise.resolve(results[0]);
}
}