0

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.

Va_M
  • 522
  • 2
  • 5
  • 18
  • 2
    What is the question? – tom10271 Sep 23 '20 at 06:20
  • `await notAPromiseValue` will result in `notAPromiseValue` – Jaromanda X Sep 23 '20 at 06:20
  • @tom10271 That why does awaiting a function that does not return a promise work. I thought await only works for functions that return a promise. – Va_M Sep 23 '20 at 06:28
  • 1
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await `If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise.` – tom10271 Sep 23 '20 at 06:31
  • Try to keep your question short and clean. The question would be `Why await returns value for function that are not returning Promise?` with a few lines of code to elaborate a bit more is good enough. – tom10271 Sep 23 '20 at 06:34
  • @tom10271 So, if the function on which `await` is called is async, will it wait till its completed? If so, my code is infact correct and not just an anomaly that works, right? – Va_M Sep 23 '20 at 06:39
  • @tom10271 Also, thanks for the link but this para `An await can split execution flow, allowing the caller of the await's function to resume execution before the deferred continuation of the await's function. After the await defers the continuation of its function, if this is the first await executed by the function, immediate execution also continues by returning to the function's caller a pending Promise for the completion of the await's function and resuming execution of that caller.` is so confusing – Va_M Sep 23 '20 at 06:40
  • Does this answer your question? [async/await implicitly returns promise?](https://stackoverflow.com/questions/35302431/async-await-implicitly-returns-promise) – tom10271 Sep 23 '20 at 06:45
  • @tom10271 Yeah I read that 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. – Va_M Sep 23 '20 at 07:02
  • @JaromandaX Thanks for the reply. So, if the function on which await is called is `async` but does not return a Promise, will `await` cause the execution to stop till that `async` function finishes its execution even if it returns a void. – Va_M Sep 23 '20 at 07:28
  • 1
    an `async` function **always** returns a Promise – Jaromanda X Sep 23 '20 at 07:31

1 Answers1

1

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  });
})();
tom10271
  • 4,222
  • 5
  • 33
  • 62
slebetman
  • 109,858
  • 19
  • 140
  • 171
  • 1
    And `await` will conveniently accept synchronous results quietly without throwing any error - this is by design and is specified in the spec – slebetman Sep 23 '20 at 07:09
  • Thanks for the reply. Anyways, I am not confused about the setState method. I know how it works but I said that so as to not make the Q too long. I said that because, the logic to update the `isValid` var in my updater function will run async. The main source of my confusion was that I can't set the `setState` function itself to be async (as you pointed out) and therefore I thought I couldn't `await` it cos its sync. And therefore I thought I couldn't await it. But as tom pointed out using `await` kinda has the same result as making that func `async`. So I think my code should work? – Va_M Sep 23 '20 at 07:23
  • @Vaibhav_M Unfortunately you did not and still do not show it is async. The only conclusion I can make is that it is not async. The function you show is exactly this: `prevstate => {}` which has zero asynchronous code inside it – slebetman Sep 23 '20 at 07:31
  • 1
    Also, `await` does not make a function async. Instead if a function is not async it simply accept the synchronous result without complaining. Tom is wrong. It is `async` that makes a function async – slebetman Sep 23 '20 at 07:32
  • Thanks, I thought so. I guess that's why I got more confused. Anyways, the `prevState => {}` is the updater function inside the setState and I thought it ran `async` and the setState itself ran `sync`. But I think as you pointed out the updater function also runs `sync` and its only the return that is `async` (which I guess is the internal react stuff). If so I guess I WAS a bit confused. But what if I made `this.setState(async prevState => {})`. Will my code work? I think not since setState is still `sync`. Thats whats so confusing, why is my code working?? – Va_M Sep 23 '20 at 07:45