Async/await implementation - javascript

I need help in async/await implementation. In my code, I want a clear button to clear everything and reload the page. I want to set this up as a promise such that only when it is fulfilled, will it send a get request to the JSON server and get the posts from the JSON server and store them in local storage.
I have set up a console.log message to pop up when the promise is resolved. Now the problem I am experiencing is this message appears and the data is fetched first then the page reload happens after. I thought the page reload would happen first and only then will the message appear and the data would get fetched. this makes the code not to function as I want. I don't know if it is the way I have set up the Promise. I hope my problem is understood. Someone help, I promise to upvote your answer and account :)
const UISelectors =uisetup.getSeletor(); //gets html classes and ids from another module
document.querySelector(UISelectors.clearBtn).addEventListener('click', clearAll); //calls event
const clearAllData = async function(){
//This is the promise supposed to be waited on
const clears = new Promise((resolve)=>{
itemsetup.clearAll();
uisetup.clearEverything()
storagesetup.clearAllfromStorage()
const addAllItems = 0
uisetup.theTotaldatas(addAllItems)
uisetup.clearEditState()
location.reload()
resolve('Finished clearing')
})
const storagedata = await clears
return storagedata
}
//Function for the clear button event
function clearAll(e){
e.preventDefault()
//Calls the function after it has been completed
clearAllData()
.then(clearmessage=> {
http.get('http://localhost:3000/posts')
.then(posts => {
storagesetup.storeinStorage(posts)
uisetup.populateItems(posts)
let datas=null
posts.forEach(post =>{
datas += post.datas
})
uisetup.theTotaldatas(datas)
console.log(posts)
})
.catch(err=>console.log(err))
console.log(clearmessage)
})
}

By looking at the following part I can say that you believe your JS process resumes after page reload.
location.reload()
resolve('Finished clearing')
This is not so. It works the other way round. That is - you create a page load hook which will execute when a page reloads and call your promise in there, take any action after the promise is resolved.
<body onload="clearAll()">
Let this function fetch data and store it in the local storage. You will have to modify your function accordingly.

Related

Promises and Async/Await don't seem to do anything in Express/node

I'm learning node and express and I'm trying to implement a function that retrieves data from a csv file uploaded by the user.
The data should be processed first and then outputted to the console, but it instead outputs an empty array before processing it.
I followed examples of async/await from tutorials I found online but the issue persists and I'm not sure how to proceed.
app.on("event:file_uploaded", async(userDataPath)=>{
let result = await retrieveDataFromCsvFile(userDataPath);
console.log(result); //should be completed after retrieving data
});
function retrieveDataFromCsvFile(path){
return new Promise((resolve)=>{
console.log(`RETRIEVING DATA FROM: ${path}`);
const stream = fs.createReadStream(path);
const rl = readline.createInterface({input: stream});
let data = [];
rl.on("line", (row)=>{
data.push(row.split(","));
})
rl.on("close", ()=>{
console.log("Data Processed"); //should be completed first
})
resolve(data);
})
}
Outputs:
RETRIEVING DATA FROM: <filepath>
[]
Data Processed
You need to call resolve when you have the data you want.
At the moment you are:
Registering a line event handler
Registering a close event handler
Calling resolve
Some time later you will get line and close events that trigger their respective event handler functions … but by then you've already resolved the promise.
You need to determine what condition means your data is ready. At a guess, I'd expect that to be "When the callback to close is run" and call resolve then.
As you can see your console logs are in the wrong order so that should tell you that the promise is resolved first and the file is processed later.
You should change the close event to
rl.on("close", ()=>{
console.log("Data Processed"); //should be completed first
resolve(data);
})
this would ensure that your promise is resolved after the function is done processing the file.

Data from firestore it's being fetched multiple times when login and logout (vanilla JS)

Well I made this Library app, where an user can login and add books. So, when a user login the app fetch data from a firestore collection, cool. The problem exists when the user login once, logout and then login again without refreshing the app. If the user do this twice, the fetch twice, if thrice, the fetch thrice. The code that executes multiple times its the fetchBooks(), the signInWithGoogle() only executes once. Here's the code involved:
function signInWithGoogle(){
const provider = new firebase.auth.GoogleAuthProvider()
auth.signInWithPopup(provider)
.then(result => {
// Create the new user document in firestore
createNewUserDocument(result.user)
// fetch feed data
auth.onAuthStateChanged(user =>{
user? fetchBooks() : null
})
}).catch(err => {
console.log(err)
})
signUpForm.reset()
signUpModal.hide()
signInForm.reset()
signInModal.hide()
}
function fetchBooks() {
const docRef = db.collection('users').doc(auth.currentUser.uid).collection('books')
docRef.get().then(querySnapshot =>{
console.log(querySnapshot)
querySnapshot.forEach(doc => {
const data = doc.data()
console.log(doc.data());
addCardToHTML(data.title, data.author, data.pages, data.description, data.read)
})
})
}
onAuthStateChanged is a subscription that triggers itself when there's a change in the user's authentication state.
So it will trigger when you log in, when you log out, etc.
So ideally you'd want to wait until the user logs in, and then call the fetchBooks() function, but if you keep doing it inside of the subscriber the function will trigger any time the subscriber emits a new value.
I would recommend starting with a restructure of your code to have functions that do individual things. Right now, you have a function signInWithGoogle. That function should only sign the user in with Google and return a promise with the result of that sign in. Instead, you have it signing in the user, fetching books (which itself is also fetching books AND modifying the DOM), and calling methods on your signUp elements.
Restructuring this to have some other top-level function would likely help you handle your problem easier. Specifically, try something like this:
function handleSignIn() {
signInWithGoogle()
.then(fetchBooks)
.then(books => {
books.forEach(book => addCardToHTML(...))
})
}
This is a good start because now it's clear what each individual function is doing. So now to handle your specific issue, I'll assume that the problem you're facing is that you're seeing the books be added multiple times. In that case, I would think what you'd want to happen is that:
When a user is signed in, you want to load their books and display them on the page.
When they log out, you want the books to be unloaded from the screen
When they log back in, the books are re-loaded and displayed.
If all of those assumptions are correct, then your problem wouldn't be with the code you have, but rather the signout functionality. When the user signs out, you need to add a function that will remove the books from the HTML. That way, when they sign back in after signing out, the handleSignIn function will kick off again and the addCardToHTML function will be running on a blank HTML page rather than a page that already has the cards.
Example:
function handleSignOut() {
signOut()
.then(clearBookCards)
}
function clearBookCards() {
// Manipulate DOM to remove all of the card HTML nodes
}

Using promise on button click in stenciljs

I am currently working on a stenciljs App and would like to get some info via a REST service. This REST call is implemented as a function which returns a Promise. Now, if I use this rest call in componentwillload Lifecycle Method, it works fine:
async componentWillLoad() {
return getUserData().then( userData => {
console.log("got userdata", userData);
});
}
Now, I would like to get this info when the user clicks a button. For that, I call this method in the click handler for this button:
<button id="mybutton" onClick={()=>this._handleButtonClick()></button>
_handleButtonClick() {
return getUserData().then( userData => {
console.log("got userdata", userData);
});
}
But this does not work, I see in the Browser console Network Tab that a network call is made but it returns no data. How can I make this thing work
Looks like you are missing closing brace (})?
onClick={()=>this._handleButtonClick() <- here
but assuming that is just a typo, maybe you can just store the result of getUserData in state object (that way, it will trigger render() function as your data change)?
Here is how I'm doing the fetch (just as an example)
fetch(`REST endpoint`, {
method: 'GET'
headers: { // Content-Type and security token }
}).then ((response: Response) => response.json())
.then(data => {
this.dataList = data;
});
(so here, I'm storing result as "dataList", which is defined with #State() dataList)
I noticed your button doesn't have an attribute type="button".
You could be running into an edge case scenario where if a button is the first button within a form, and isn't marked as type="button", the browser defaults it to <button type="submit">. At that point, clicking on the button might trigger the API call because of the registered listener, but it'll also trigger a form submit.
Since API calls take some time to complete, by the time the response is returned your page may have reloaded because of a form submit.
Again, this is an edge case scenario but looks like a reasonable explanation for what you're observing.

Cypress - wait for the API response and verify UI changes

I have a component that I want to cover with some e2e tests. This component takes the URL provided by the user in the input, calls the API after the button click and then returns the shortened version of that URL. After that, shortened url is added to the list below the input on the UI and makes some localStorage assertion. I want Cypress to wait for the API response and only then check the UI if the list item was added. I made this working but I hardcoded the wait time in the wait() method. How Can I achieve that programatically ?
describe("Shortener component", () => {
it("Should add the list item and data to localStorage", () => {
cy.visit("http://127.0.0.1:5500"); //Live server extension address
cy.get("#url-input").type("https://facebook.com");
cy.get("#form-submit-button").click();
// wait for the api response and make sure that the value has been added to the localStorage
cy.wait(40000); //todo - wait for the api response instead of hardcoding the wait time
const localStorageData = localStorage.getItem("linksData");
if (JSON.parse(localStorageData)) {
expect(JSON.parse(localStorageData)[0].inputValue).to.eq(
"https://facebook.com"
);
}
// check if the new list item with the corrct value has been addded
cy.get(".shortener-component__list-item")
.contains("https://facebook.com")
.should("be.visible");
//validation mesasge should not be visible
cy.get("#validationMesage")
.contains("Please add a valid link")
.should("not.be.visible");
});
});
I tried with intercept() however I failed. Not sure how to make it working. I also saw some similar SE topics on that but it did not help me.
Any ideas / examples apreciated :)
Thx !
From the order of events you've given
short URL returned
added to localStorage
added to list
just change the order of feature testing
test list - it is last event, but has retriable commands (you can increase the timeout)
now test localStorage, if UI has the short URL so will localStorage
cy.contains('.shortener-component__list-item', 'https://facebook.com', { timeout: 40000 })
.then(() => {
// nested inside .then() so that the above passes first
const localStorageData = localStorage.getItem("linksData");
const linksData = JSON.parse(localStorageData);
expect(linksData).not.to.eq(undefined);
expect(linksData[0].inputValue).to.eq("https://facebook.com");
})
Alternatively, to make use of retry and timeout on the localStorage check,
cy.wrap(localStorage.getItem("linksData"))
.should('not.eq', undefined, { timeout: 40000 }) // will retry the above until true
.then(data => JSON.parse(data))
.should(parsedData => {
expect(parsedData.inputValue).to.eq("https://facebook.com");
});
I guess you should also start the test with
cy.clearLocalStorage("linksData")
There're examples in the documentation, it only takes some reading and experimentation.
In general, you need three commands: cy.intercept(), .as(), and cy.wait():
cy.intercept(your_url).as('getShortenedUrl');
cy.wait('#getShortenedUrl');
you can also use .then() to access the interception object, e.g. a response:
cy.wait('#getShortenedUrl').then(interception => { });
or you can check something in the response using .its():
cy.wait('#getShortenedUrl').its('response.statusCode').should('eq', 200);
The point is that after cy.wait('#getShortenedUrl'), the response has been received.

How to get the result from firebase first and then redirect to another page?

i have a function in my app to get some data from firebase and after completion of that firebase on function, i need to redirect it to another page.
But in my code when i press the button it will quickly redirect to another bage so the function is not processed. How can i do this?
$('#pg-myb-inde #testBtn').on('click',function(){
myFirebaseRef.orderByChild('emailid').equalTo('mishan#gmail.com').on('value',function(userSnap){
var user=userSnap.val();
// $('#UserPage .person-name').empty();
// $('#UserPage .person-name').append('' +
// '<i class="glyphicon glyphicon-th-list"></i> '+user.fname);
console.log('appended');
console.log(user.fname);
});
location.href = "/another-web-page";
})
Is this what you're looking for?
$('#pg-myb-inde #testBtn').on('click',function(){
var query = myFirebaseRef.orderByChild('emailid').equalTo('mishan#gmail.com');
query.on('value',function(userSnap){
var user=userSnap.val();
location.href = "/another-web-page";
});
})
The only thing I've changed is move the setting of the location into the callback function.
If that is indeed what you're looking for, it may help if you put some logging statements into this code:
$('#pg-myb-inde #testBtn').on('click',function(){
var query = myFirebaseRef.orderByChild('emailid').equalTo('mishan#gmail.com');
console.log('Before Firebase query');
query.on('value',function(userSnap){
console.log('In Firebase callback');
var user=userSnap.val();
location.href = "/another-web-page";
});
console.log('After Firebase query');
})
Now when you run this code, the output is most likely not what you expected:
Before Firebase query
After Firebase query
In Firebase callback
The reason for this is that the data is asynchronously retrieved from Firebase. When you call on() it starts the request to the server. But since it may take a while before the response comes back from the server, your browser doesn't wait for the answer and just continues with the code after the query. When when the data come back from the server, your callback is invoked.

Categories

Resources