Accepted answer

I tried to do my best to simplify and complement Davin's answer, so you can get a better idea of what is actually going on here:

  1. await is placed in front of this.handleChange, this will schedule the execution of the rest of changeAndValidate function to only run when await resolves the value specified to the right of it, in this case the value returned by this.handleChange
  2. this.handleChange, on the right of await, executes:

    2.1. setState runs its updater but because setState does not guarantee to update immediately it potentially schedules the update to happen at a later time (it doesn't matter if it's immediate or at a later point in time, all that matters is that it's scheduled)

    2.2. console.log('synchronous code') runs...

    2.3. this.handleChange then exits returning undefined (returns undefined because functions return undefined unless explicitly specified otherwise)

  3. await then takes this undefined and since it's not a promise it converts it into a resolved promise, using Promise.resolve(undefined) and waits for it - it's not immediately available because behind the scenes it gets passed to its .then method which is asynchronous:

“Callbacks passed into a promise will never be called before the completion of the current run of the JavaScript event loop”

3.1. this means that undefined will get placed into the back of the event queue, (which means it’s now behind our setState updater in the event queue…)

  1. event loop finally reaches and picks up our setState update, which now executes...

  2. event loop reaches and picks up undefined, which evaluates to undefined (we could store this if we wanted, hence the = commonly used in front of await to store the resolved result)

    5.1. Promise.resolve() is now finished, which means await is no longer in affect, so the rest of the function can resume

  3. your validation code runs


enter image description here

enter image description here

enter image description here

Take a simple example: in the test-02, the code await this.setState({ a: 10 }); In fact, is equal to this.setState({ a: 10 }); await undefined; in the test-03. And await undefined; causes the code behind it to be executed in the next event loop.


The rv or return value of await is defined as:

Returns the fulfilled value of the promise, or the value itself if it's not a Promise.

So since handleChange is not an async or promise value, it's simply returning the natural value (in this case, there is no return, so undefined). Thus, there is no async event loop trigger here to "let it know handleChange is done", it simply just runs in the order you have given it.


setState() does not always immediately update the component doc

But it may be the case here.

If you want to replace the callback with a promise you can implement it yourself :

setStateAsync(state) {
  return new Promise((resolve) => {
    this.setState(state, resolve)

handleChange = (e) => {
  return this.setStateAsync({[]:})

ref :


I haven't tested this yet, but here is what I think is happening:

The undefined returned by await is queued after the setState callback. The await is doing a Promise.resolve underneath (in the regenerator-runtime) which in turn yields control to the next item on the event loop.

So, it is coincidence that the setState callback happens to be queued ahead of the await.

You can test this by putting a setTimeout(f => f, 0) around the setState.

regenerator-runtime in babel is essentially a loop that uses Promise.resolve to yield control. You can see the inside the _asyncToGenerator, it has a Promise.resolve.

Related Query

More Query from same tag