Will await wait till the completion of function NOT returing a promise - javascript

question is: if a function runs async but does not return a promise itself (returns void), then will "awaiting" it still work. I thought that 'await' only works if the function returns a promise and not void.
So, in my async function in react I am doing something like this
class Form extends Component {
submitForm = async event => {
event.preventDefault();
const isFormValid = await this.validateForm();
if(!isFormValid) {
return;
}
//some logic
}
validateForm = async () => {
let isValid = false;
//some logic
await this.setState(prevState => {});
//some more logic
return isValid; //this is set inside the setState
}
}
Here I am awaiting this.setState(), but this.setState() does not return a promise, although it is a function that runs asynchronously. At first I thought this would not work. But after debugging and trying it out for a few times, it seems to work. The isValid value is returned only when the this.setState completes its execution (thus successfully setting the isValid value internally depending upon the state).
Although my code is working, it has left me kind of confused, as though why this is working. I read somewhere that await waits till the value on the right side is resolved. But I thought that was only applicable if the expression on the right side was a Promise.
I thought a more valid or correct way was as described in this article I found https://medium.com/front-end-weekly/async-await-with-react-lifecycle-methods-802e7760d802 which makes sense since the custom setStateAsync function returns a promise.
For those of you who can't access the article here is the relevant snippet
class AwesomeProject extends Component {
state = {}
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
async componentDidMount() {
StatusBar.setNetworkActivityIndicatorVisible(true)
const res = await fetch('https://api.ipify.org?format=json')
const {ip} = await res.json()
await this.setStateAsync({ipAddress: ip})
StatusBar.setNetworkActivityIndicatorVisible(false)
}
}
edit: (title changed from "Using await for functions NOT returing a promise")
edit2: I read async/await implicitly returns promise? question before I posted my question. I do get that assigning a function the async keyword turns it into something that returns a promise. But I didn't know that just using the await keyword also had the same effect. As this is not mentioned anywhere that I looked. As I said, if setState was some function that I had defined, I get that I could turn it into a promise returning function by assigning the async keyword to it. But I didn't know I could do the same by not assigning the async but just using await while calling it.

TLDR
Basically, the reason it works is because it is synchronous.
but this.setState() does not return a promise, although it is a function that runs asynchronously
This is where you are confused. The this.setState() method is not asynchronous. Or rather, it runs the function you pass to it synchronously but will defer processing the result of that function (the new state that function returns) until the next this.render() cycle.
So depending on what you mean this.setState() can be said to be either synchronous or asynchronous:
function statechanger (prevstate) {
return { foo: bar } // this return value will be handled in the future
// in other words, React will process this return
// value asynchronously
}
this.setState(statechanger); // statechanger() will be called synchronously here
React does not support asynchronous setState() even though the result of setState will be processed asynchronously. If you ever need to use setState asynchronously then you need to use regular asynchronous features of javascript instead of anything provided by setState, eg:
something_async((result) => this.setState({ something: result }))
// or
something_async().then((result) => this.setState({ something: result }))
// or
(async () => {
let result = await something_async();
this.setState({ something: result });
})();

Related

Repeatedly call a function that returns a promise until one of them contains a specific response

Hello I need to call a REST function with an ID, that returns a promise in React.js. This function will at some point contain a certain value in its response when called . Until another service has processed an initial request this value will be null.
This is what I have done so far:
while(myVariable){
myfunction(myID).then( (response) => {
if(response['value'] != null
myVariable = false;
}
});
}
The problem with this code is that the while loop is called as fast as possible and thus completely utilises the computer. So I need a function that allows me to poll for a result by an ID until the response of one of the function calls contains a valid response.
I have tried the following method but without success, because I don't have a fixed number of runs:
Wait promise inside for loop
Thanks in regards.
As you state, the problem is that the while loop runs eagerly, not waiting for each promise to resolve.
One way to solve that is to use recursion. Recursion gives you more control over when exactly you want to 'loop' next:
let getValue = () => {
myFunction(myID).then(response => {
if (response['value'] === null) {
setTimeout(getValue);
} else {
// here you know the other service has processed the initial request
}
});
};
First I wrapped the whole thing in a function called getValue. Note that this function is only called again after the promise resolves. (The call to setTimeout is a trick to use recursion without consuming the stack.) If this still runs too quickly, pass an additional parameter of 100 or so to the setTimeout invocation.
Alternatively, you may be able to use async/await similarly to the link you shared. I'm no expert on async/await but it should work the same with while loops as with for loops, judging by this and this.
You can use the async function with await.
I also use a delay function to delay each call to the myfunction().
While you get a response, you can break the while loop.
const delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
async function main() {
const myID = 1;
let response;
while (true) {
response = await myfunction(myID);
if (response["value"] != null) {
break;
}
await delay(5000);
}
//do Something once you get the response here below:
}
main();

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")
}
})

passing data with async/await in action function

I have the following two functions:
async function queryData(){
const query= await axios.get('...')
const queryStatus = portNames.map(...);
const dataStatus= await Promise.all(queryStatus);
return dataStatus;
}
export function actionData(){
const data = queryData();
return{
type:cst.RECEIVE_DATA,
payload:data
}
}
queryData() function return after some line code some data in promise...in the second function i put the data in payload for sending my action to reducer.
the problem is that when i'm trying to pass my data from first function in two second, if i output my variable in console.log() inside the second function,it shows:
instead if i try to print my variable inside the first function
i'm able to access my value from promise...what could be the problem that might create promise pending in actionData()?..therfore how can i pass my data value from promise to action in a way to dispatch my action with relative data to reducer?
Asynchronous functions always return promises. If you want to get access to the value they contain, you either need to call .then on the promise, or put your code in an async function and await the promise.
Since you are using redux, there are some additional considerations. By default, redux does everything synchronously. Dispatching an action should synchronously go through the reducers and update the state. To do async things with redux you'll need to add a middleware. There are a few possible async middlewares, but the one recommended by the redux team is redux-thunk.
With redux-thunk in your project, instead of dispatching an action object, you can dispatch a function. That function is empowered to do async things and dispatch actions when it's done. So a possible implementation for your case would be something like this:
function actionData() {
return async function(dispatch) {
const data = await queryData();
dispatch({
type: cst.RECEIVE_DATA,
payload: data
});
}
}
Let me explain to you the flow here.
First we come here
const data = queryData();
In queryData function we have:
async function queryData(){
const query= await axios.get('...') <------------ first breakpoint ----------------
// ......
}
When this async req is hit, the code doesn't stop it's flow, it continues. Next return statement is hit.
return {
type:cst.RECEIVE_DATA,
payload:data // data is undefined at this moment
}
In the meantime the async request's response comes back. We sequentially execute the next 3 statements of queryData() function
const queryStatus = portNames.map(...);
const dataStatus= await Promise.all(queryStatus);
return dataStatus;
And now the data variable gets updated.
To ensure proper flow, you could write something like this:
export async function actionData() {
const data = await queryData();
return {
type:cst.RECEIVE_DATA,
payload:data
}
}
I'd encourage to you read about async/await on MDN.

Why Does Async Always Return a Promise?

This question is theoretical - I have no concrete problem to solve.
With that said, why does the async keyword wrap the return value of an async function in a promise? What's the point? Is it ONLY because the await expression expects a promise? Or is there some meaning / use behind this decision?
I thought i'd answer this primarily because async in Javascript used to confuse the hell out of me, and all of a sudden it snapped, so i hope this analogy may help this happen for you.
You have an async event. This could be anything, getting something from a server, doing something in the browser that takes time, training a machine learning model (!), executing a function or method that uses a setTimeout etc.
The beauty of Javascript and a key reason it works so well for the browser is that it uses the processor thread it runs on in a very clever way that stops the thread from getting blocked by processes that take time (like the ones mentioned above)
Many other languages, for example Ruby run on more than one thread. It is possible to use service workers to run processes on multiple threads in javascript but that is outside the scope of this answer!
The async nature of the JS event loop allows the thread to 'go off' and do something else while it is waiting for a process to finish.
The problem with this from a programming point of view is that it is possible for something in the code that relies on the result of a blocking event to get 'undefined' as a result of the event if it doesn't wait for the event to finish before it tries to use the result of it. Take this piece of code below
let scopedVariable
console.log('the code has started')
setTimeout(() => {
scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)
When the code reaches the console log, the setTimeout hasn't yet completed. As the setTimeout sets the scopedVariable only when it completes, the variable is undefined when we log it
if however
We wrap the timeout in a promise we can await it's resolve callback (first argument of promise) and the code will 'pause' until the promise reaches the resolve callback before continuing.
When we await the promise and the setTimeout completes, the resolve function sets the variable, so that when we console log it it holds the value from the promise
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
const result = await asyncEvent
console.log(scopedVariable)
}
container()
You can use await and .then interchangably
For example we could go:
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
asyncEvent.then(() => console.log(scopedVariable))
}
container()
once again the code will pause at .then and then continue when the asyncEvent promise has resolved.
In fact if we use .then we don't need to enclose it in an async function so we can rewrite it like this
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
asyncEvent.then(() => console.log(scopedVariable))
The great thing about .then is that the accompanying .catch allows you to catch any errors thrown by the async event (for example if retrieving something from a server when there is an error). For async await you need to wrap potentially dangerous functions in a try catch.
In order to use await you need to be inside an async function (hence the async container function above). This is not necessary with .then, but .then and .catch chains can make your code messy.
I hope this helps!
The async and await operators are just syntactic sugar that hide the underlying use of promises to implement asynchronous code.
Using async before a function definition makes the function return a promise that resolves to the function's return value, rather than returning normally.
Using await before an asynchronous function call suspends the current function until the promise that it returns is resolved. It's basically equivalent to wrapping the remainder of the function in an anonymous function, and using that as the .then() callback of the promise.
For more information between the relationship, see How to translate Promise code to async await

Is it legitimate to omit the 'await' in some cases?

I am using async/await in several places in my code.
For example, if I have this function:
async function func(x) {
...
return y;
}
Then I always call it as follows:
async function func2(x) {
let y = await func(x);
...
}
I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
For example:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
Is this conclusion correct, or else, what am I missing here?
EDIT:
A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:
It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.
For example, removing the await in the code below is "dangerous":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.
If func is an async function then calling it with and without await has different effects.
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).
func(1).then(z => /* use z here */)
If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.
await just lets you to treat promises as values, when used inside an async function.
On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).
So:
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.
So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.
So yes, is always safe to drop await in the last return statement.
PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.
PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.
An async function always returns a Promise.
So please keep in mind that these writing of an async function are all the same:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.
As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.
Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.
Compare these two examples within the scope of an async function:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
with:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.
In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.
(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)
I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.
I got a good answer above, here is just another explanation which has occurred to me.
Suppose I have this:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
The reason why I can safely remove the await in the return statement on func2, is that I already have an await when I call func2 in func3.
So essentially, in func3 above I have something like await await func(x).
Of course, there is no harm in that, so it's probably better to keep the await in order to ensure desired operation.

Categories