Wait for function to execute before unloading javascript - javascript

I have a piece of code which i wish to run before the browser refreshes or redirects
window.addEventListener("beforeunload", async (event) => {
await this.save()
});
the method i'm running involves parsing JSON , updating it , and saving it back to the sessionStorage.Is there a way to make sure this.save() completes before unloading? or at least have it run in the background while the page redirects.
I have tried removing the async and await code, but does not work.
EDIT here is the save method:
saveCanvas = async (call_back_) => {
const FlowState = JSON.parse(sessionStorage.getItem('FLOWSTATE'))
...
...
await html2canvas(div).then((screenshot) => {
...
...
sessionStorage.setItem('FLOWSTATE', JSON.stringify(FlowState))
})
}
}

beforeload is the final event before leaving the page(reload leave and reenter for javascript runtime). if you use async functions(include async-await, promise, settimeout) in the event listening functions, it can't be executed anymore.
I think there may be async executions in save function, so it won't work even if you remove async-await keyword.

EDITED
Now, I actually suggest you change the layout.. the logic is to have a while loop awaiting saveCanvas(because while behaves well with await). So when the page is unloading, the last html2canvas(div) would be saved
saveCanvas = async(call_back_) => {
const FlowState = JSON.parse(sessionStorage.getItem('FLOWSTATE'))
await html2canvas(div)
sessionStorage.setItem('FLOWSTATE', JSON.stringify(FlowState))
}
let toRun=1
while(i){await saveCanvas()}
window.addEventListener("beforeunload", (event) => { toRun=0 });

Related

Waiting for another async function to finish before running

I have two functions that I want to run one after the other finishes. I'm using webdriver.IO so I need to wait for one function to log into the page, and then I want another function to run.
Other questions here on SOF are telling me to use a promise, however I need to use a promise, however when I wrap my functions in a promise I get the error SyntaxError: await is only valid in async function.
I have a login function
const Login = async () => {
const browser = await remote({
capabilities: {
browserName: 'chrome'
}
})
const userName = await browser.$('#username')
const password = await browser.$('#password')
await userName.setValue(process.env.NAMEUSERNAME)
await password.setValue(process.env.PASSWORD)
const loginButton = await browser.$('button[type="submit"]')
await loginButton.click()
}
Once this completes and the page loads I want to run another function.
I have gotten this to work with a setTimeout however I don't want to just have a chain of setTimeout in my application
It's hard to give you precise code without seeing where this is called from: but presuming it's inside some other function, you just have to mark that one async and use await inside it:
async theOuterFunction() {
await Login();
doTheOtherThing();
}
(If doTheOtherThing also returns a promise you could await that too - and would need to if you want to run other code inside theOuterFunction after that. But there's no need if it's the last statement inside the function.)

How can I make a statement wait until a previous function is finished?

I've got an issue where window.open gets called too quickly and my other function doesn't finish and post in time in my onclick function.
I tried setting a timeout on the trackData() but it only worked on occasion and I didn't want to set a longer timeout.
onClick
{() => {
trackData();
window.open("https:google.com", "_self");
})
any ideas?
EDIT: The following works locally but doesn't track when in production. Tracking works EVERYTIME if "_self" is being replaced with "_blank" (which it cannot be)
let postData = async(value) => {
await tracker({
action: value,
})
}
tracker just makes an axios post with the action
<div
className="exampleBTN"
onClick={() => {
postData("example").then(
window.open("https://google.com",
"_self")
)}
}
>
</div>
Locally, I can see the data going into the database.
However, online it doesn't work. It only works if either of these are true:
Doesn't have window.open in the onClick
doesn't have "_self" but "_blank" so it opens in a new tab
I thought my async was wrong so I also tried the following:
onClick={async () => {
await postData("example").then(
window.open("google.com", "_self"))
}}
You can work with .then or async/await to do exactly this when action results in a Promise. Axios requests return Promises.
initiateAsynchronousAction()
.then(result => console.log(result))
Callback inside the .then function will only be executed if the promise is fulfilled by the action executed by the async function.
Minor clarification:
Note that inside the .then() you have to pass a callback function and not just immediately invoke actions you want to perform, even if you don't plan to use the result value. So it will be
.then(result=> console.log('Inside callback!'))
and not
.then(console.log('Inside callback!'))
Async-await is another way to write this, it is simply syntactic sugar, that is just an easier way to write it:
const foo = async () => {
const result = await initiateAsynchronousAction()
console.log(result)
}
The variable result will only be given the value once the Promise is resolved. That is, the assigning of the value will be awaited.
You can chain a .catch in the first case or envelop the task in a try-catch block to catch errors if the Axios promise is unfulfilled.
you can use promise based approache here.use aysnc and await for that.
async function trackData(){
.....
let data = awiat your code
.....
}
function call with promise
trackData().then(res=>{
if(res ==="Success){
window.open("https:google.com", "_self")
}
})

Await socket.on('answerToRequest') possible with Socket.io?

Is the following somehow possible?
async function doesSocketAgree(){
socket.emit('doesOtherSocketAgree', otherSocketId);
await socket.on('responseDoesSocketAgree', (answer)=>{
console.log('answer');
});
}
In socket.io you can use "acknowledgements" callbacks:
async function doesSocketAgree(){
await new Promise(resolve => {
socket.emit('doesOtherSocketAgree', otherSocketId, (answer) => {
resolve(answer);
});
});
}
https://socket.io/docs/#Sending-and-getting-data-acknowledgements
So you can use a single emit() and can trigger a callback.
That has the advantage you dont have to deal with memory leaks from register an event listener every time you call this function.
It is, but not that way. You'll have to wrap things in a promise so that you can "return" from your await once data comes in as part of your "on" handling:
async function doesSocketAgree(){
socket.emit('doesOtherSocketAgree', otherSocketId);
await new Promise(resolve => {
socket.on('responseDoesSocketAgree', answer => {
resolve(answer);
});
});
}
And you probably want to remove that listener before you call resolve() so that it doesn't keep on triggering, because every time you call doesSocketAgree() you'd be adding a new listener to the "on:responseSocketAgree" pile. So that'll end up going wrong pretty quickly without cleanup.
On that note, you probably want to emit your "does it agree?" with a random token that your on handler can verify is the one that's scoped to the current function call, "because async".

socket.io control execution order with nested async and await

In my app, I have multiple users submitting data "at once" via a socket.event.
Because I need to know if every user has submitted their data successfully, I try to indentify the first and last entry.
For that, I call an async function on the event, which itself includes "await".
Basically, the function looks for a value in the database and then does something depending on whether it exists or not.
Because of that, I need the function block (with its awaits) to be fully executed before the next instance of the function is called.
However, what I found is that the as soon as I call await inside the function, the next instance of said function starts executing, which obviously messes everything up.
socket.on("entries", async entries => {
const firstEntry = await match.noEntries(mid);
if (firstEntry) {
//do sth
}
await match.submitEntries(mid, socket.pid, entries);
});
//FUNCTION
noEntries: async (mid) => {
const entries = await database.query("...");
return (entries.length == 0);
}
Is there any way I can prevent the next instance of the function to be executed before it's predecessor has finished?
Thanks in advance!

Sleep function for React-Native?

So I'm trying to fetch all 'places' given some location in React Native via the Google Places API. The problem is that after making the first call to the API, Google only returns 20 entries, and then returns a next_page_token, to be appended to the same API call url. So, I make another request to get the next 20 locations right after, but there is a small delay (1-3 seconds) until the token actually becomes valid, so my request errors.
I tried doing:
this.setTimeout(() => {this.setState({timePassed: true})}, 3000);
But it's completely ignored by the app...any suggestions?
Update
I do this in my componentWillMount function (after defining the variables of course), and call the setTimeout right after this line.
axios.get(baseUrl)
.then((response) => {
this.setState({places: response.data.results, nextPageToken: response.data.next_page_token });
});
What I understood is that you are trying to make a fetch based on the result of another fetch. So, your solution is to use a TimeOut to guess when the request will finish and then do another request, right ?
If yes, maybe this isn't the best solution to your problem. But the following code is how I do to use timeouts:
// Without "this"
setTimeout(someMethod,
2000
)
The approach I would take is to wait until the fetch finishes, then I would use the callback to the same fetch again with different parameters, in your case, the nextPageToken. I do this using the ES7 async & await syntax.
// Remember to add some stop condition on this recursive method.
async fetchData(nextPageToken){
try {
var result = await fetch(URL)
// Do whatever you want with this result, including getting the next token or updating the UI (via setting the State)
fetchData(result.nextPageToken)
} catch(e){
// Show an error message
}
}
If I misunderstood something or you have any questions, feel free to ask!
I hope it helps.
try this it worked for me:
async componentDidMount() {
const data = await this.performTimeConsumingTask();
if (data !== null) {
// alert('Moved to next Screen here');
this.props.navigator.push({
screen:"Project1.AuthScreen"})
}
}
performTimeConsumingTask = async() => {
return new Promise((resolve) =>
setTimeout(
() => { resolve('result') },
3000
)
);
}

Categories