Async Await not awaiting for async method to occur - javascript

I am building up a shopping website for a project. I currently am writing a loadcart method using javascript. Basically what happens is that I store my cart data in my session storage. If no shopping cart data is found in session storage I fetch from the database. I made the function an async function because my getshoppingcartDB function is an asynchronous method.
When there is no data in session storage, I expected my method to enter the if clause, fetch the data from the database and update the cart array. However, my cart array is still blank. I notice that the cart array is ONLY updated later as shown by the screenshot.
As such, Im a litte curious how the await function? I thought the await is supposed to wait for a response back first but it seems here that it is being executed asynchronously.
This is my load cart function
// Load cart
cart = [];
async function loadCart() {
console.log("LOAD CART")
var uname = document.getElementById("username").innerText
uname = uname.trim();
if (sessionStorage.getItem("shoppingCart") === null) {
console.log("check null")
const cartdata= await getshoppingcartDB(uname);
cart = cartdata
}else{
cart = JSON.parse(sessionStorage.getItem('shoppingCart'));
}
return cart
}
loadCart().then(function(response){
cart = response
})
This is my async function for retrieving data from the database
async function getshoppingcartDB(email) {
let params = {
"email": email
};
let query = Object.keys(params)
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
.join('&');
let url = '/getcartdatabyemail?' + query;
const response = await fetch(url);
return response.json()
}
PS: I edited my function as per Mike's advice but stll doesnt seem to work? I also cant seem to do a result = await loadCart().

Related

Prevent fetch request if browser session variable exists

I have a function that pulls the users location based on an API request and stores it as a session variable in the browser. Then, it gets the phone number based on the users location which is stored in the session variable. However, I'm trying to prevent the fetch requests from being made if the browser has the session variable stored, to prevent unnecessary API requests.
I'd tried checking if the country_code variable exists in the session storage, but that seems to lock up the subsequent requests:
async function fetchCountryAndPhone() {
if (!sessionStorage.getItem("country_code")) {
const [countryCode, phoneNum] = await Promise.all([
fetch('/userinfo/'),
fetch('/contacts/')
]);
const country = await countryCode.json();
sessionStorage.setItem("country_code", country.country_code.toLowerCase())
}
const phone = await phoneNum.json();
return [phone];
}
fetchCountryAndPhone().then(([phone]) => {
let getCountryCode = sessionStorage.getItem("country_code");
let getContactNum = phone[`${getCountryCode}`].sales
....
}).catch(error => console.warn(error));
How can I adjust the logic to prevent subsequent fetch requests if the country_code session variable exists, and just use the one that's already been stored?
Save both country_code and phone in session storage.
async function fetchCountryAndPhone() {
if (!sessionStorage.getItem("country_code")) {
const [countryCode, phoneNum] = await Promise.all([
fetch('/userinfo/'),
fetch('/contacts/')
]);
const country = await countryCode.json();
sessionStorage.setItem("country_code", country.country_code.toLowerCase());
const phone = await phoneNum.json();
sessionStorage.setItem("phone", JSON.stringify(phone));
}
let phone = JSON.parse(sessionStorage.getItem("phone"));
return [phone];
}

Perform fetch request within a Firestore transaction: receiving "Cannot modify a WriteBatch that has been committed"

I'm trying to perform a fetch request within a transaction but when the code executes I receive the following error.
Error: Cannot modify a WriteBatch that has been committed.
The steps the function is performing are the following:
Compute document references (taken from an external source)
Query the documents available in Firestore
Verify if document exists
Fetch for further details (lazy loading mechanism)
Start populating first level collection
Start populating second level collection
Below the code I'm using.
await firestore.runTransaction(async (transaction) => {
// 1. Compute document references
const docRefs = computeDocRefs(colName, itemsDict);
// 2. Query the documents available in Firestore
const snapshots = await transaction.getAll(...docRefs);
snapshots.forEach(async (snapshot) => {
// 3. Verify if document exists
if (!snapshot.exists) {
console.log(snapshot.id + " does not exists");
const item = itemsDict[snapshot.id];
if (item) {
// 4. Fetch for further details
const response = await fetchData(item.detailUrl);
const detailItemsDict = prepareDetailPageData(response);
// 5. Start populating first level collection
transaction.set(snapshot.ref, {
index: item.index,
detailUrl: item.detailUrl,
title: item.title,
});
// 6. Start populating second level collection
const subColRef = colRef.doc(snapshot.id).collection(subColName);
detailItemsDict.detailItems.forEach((detailItem) => {
const subColDocRef = subColRef.doc();
transaction.set(subColDocRef, {
title: detailItem.title,
pdfUrl: detailItem.pdfUrl,
});
});
}
} else {
console.log(snapshot.id + " exists");
}
});
});
computeDocRefs is described below
function computeDocRefs(colName, itemsDict) {
const identifiers = Object.keys(itemsDict);
const docRefs = identifiers.map((identifier) => {
const docId = `${colName}/${identifier}`
return firestore.doc(docId);
});
return docRefs;
}
while fetchData uses axios under the hood
async function fetchData(url) {
const response = await axios(url);
if (response.status !== 200) {
throw new Error('Fetched data failed!');
}
return response;
}
prepareMainPageData and prepareDetailPageData are functions that prepare the data normalizing them.
If I comment the await fetchData(item.detailUrl), the first level collection with all the documents associated to it are stored correctly.
On the contrary with await fetchData(item.detailUrl) the errors happens below the following comment: // 5. Start populating first level collection.
The order of the operation are important since I do now want to make the second call if not necessary.
Are you able to guide me towards the correct solution?
The problem is due to the fact that forEach and async/await do not work well together. For example: Using async/await with a forEach loop.
Now I've completely changed the approach I'm following and now it works smoothly.
The code now is like the following:
// Read transaction to retrieve the items that are not yet available in Firestore
const itemsToFetch = await readItemsToFetch(itemsDict, colName);
// Merge the items previously retrieved to grab additional details through fetch network calls
const fetchedItems = await aggregateItemsToFetch(itemsToFetch);
// Write transaction (Batched Write) to save items into Firestore
const result = await writeFetchedItems(fetchedItems, colName, subColName);
A big thanks goes to Doug Stevenson and Renaud Tarnec.

Firebase Realtime Database not saving data when I add "location.replace"

I am trying to push data to Firebase Real-time Database and after the data is pushed(and saved), the browser should open another page. I have used "location.replace()" function to open the next page however adding the location.replace line makes the data not to be saved in Firebase real-time database.
Here is my code
var updates = {};
updates['/users/' + document.getElementById('username').value] = data;
firebase.database().ref().update(updates);
console.log("Saved successfully")
location.replace("nextpage.html");
The update function is asyncronous; it will take some time to complete. If you want to wait until the update is done, then you will need to use the promise it returns:
var updates = {};
updates['/users/' + document.getElementById('username').value] = data;
firebase.database().ref().update(updates)
.then(() => {
console.log('Saved successfully');
location.replace('nextpage.html');
});
Or with async/await:
async function someFunction () {
var updates = {};
updates['/users/' + document.getElementById('username').value] = data;
await firebase.database().ref().update(updates);
console.log("Saved successfully")
location.replace("nextpage.html");
}
the update seems to be asynchronous function, returning a promise.
so you should properly handle it. Otherwise call to "location" may come before the update is finished.
Change it like this:
firebase.database().ref().update(updates).then(() => {
console.log("Saved successfully")
location.replace("nextpage.html");
}).catch(error => {console.log('Error:', error)})
If you don't want to use promise, provide an additional argument to the update function that will serve as a callback function, i.e will be called when update is finished:
firebase.database().ref().update(updates, function() {
console.log("Saved successfully")
location.replace("nextpage.html");
})

Vue & Firebase: Data being duplicated in state unexpectedly

I have two calls to Firebase: one to get the existing data and one to listen for updates in the data. When those updates happen, instead of replacing the existing data for some reason I see to be adding the two datasets together. Can't figure out why as I'm directly updating state with new data in my second function.
Here are the functions called on mounted():
mounted() {
this.getImages();
this.refreshImages();
},
And the two functions in question:
async getImages() {
let snapshot = await db
.collection("Maria")
.orderBy("timestamp", "desc")
.get();
snapshot.forEach((doc) => {
let appData = doc.data();
appData.id = doc.id;
this.picturesData.push(appData);
});
this.dataLoaded = true;
},
async refreshImages() {
await db
.collection("Maria")
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
let newPicturesData = [];
snapshot.forEach((doc) => {
let newPictureData = doc.data();
newPictureData.id = doc.id;
newPicturesData.push(newPictureData);
});
this.picturesData = newPicturesData; // this should overwrite the data in my state, right? But instead it's appending.
});
},
It's difficult to tell you exactly what's happening without thoroughly testing your code but you have to note that the two calls (to getImages() and refreshImages()) may not be done in the order you expect.
Since in getImages() you push the data to picturesData and in refreshImages() you replace picturesData, I suspect that the listener set through refreshImages() returns data before you get the result of the query triggered by getImages().
Actually, since onSnapshot() triggers an initial call that returns the entire result of the query, you only need to call refreshImages() (you don't need the initial call to getImages()).
Note that onSnapshot() is not an asynchronous method like get(), so you don't need to make refreshImages() async.

async function has old state React-Redux

I am working on a React project where user can upload and remove photos in projects. After Uploading new image it should be visible to user only if corresponding projectis selected. The solution is fairly simple to check
if
(selectedProject.projectID=== imageMetadata.projectID)
where
selectedProject.projectID: Id of currently selected project( Comming from
Redux Store )
imageMetadata.projectID: Id of project to which uploaded image belongs.
All of this is being done inside an async function and the problem we are facing is that even after selectedAlbum.albumID is changed everywhere else, this function still has its old value. Is it because the function is async?
This is my code:
let projectId = selectedProject.projectID;
const responses = await Promise.all(
filesToUpload.map( async (file) => {
let res = await imageStoreApiHandler.uploadPhoto(file, projectId);
notifyUploadProgress(count++, total, notifcationKey);
if (res.status === 200) {
let imageMetadata: any = res.data[0];
if (selectedProject.projectID === imageMetadata.projectID) {
addImage(imageMetadata);
}
}
return res;
}));
It's probably a closure problem where the uploadhandler keeps caching the first function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
Maybe write a function which will return a promise all array based on the parameters current project id.

Categories

Resources