What is the async?


There are some problems with asynchronous operations in everyday work these days. Over the course of time, asynchrony has been handled in many ways: callback functions, Promise chained syntax, Generator functions, and now the more popular async functions. So what is async?

Async functions are syntactic sugar for Generator functions. Use the async keyword instead of the asterisk * of Generator functions and the await keyword instead of yield. Compared to Generator functions, async functions have the following four improvements:

  • Built-in actuator GeneratorFunction execution must depend on the executor, hence the existence ofcoModule, andasyncFunctions come with their own actuators.
  • Better semantics asyncawaitCompared to*yield, the semantics are clearer.asyncIt means that there are asynchronous operations in the function,awaitIndicates that the following expression needs to wait for the result.
  • Wider applicability coModule convention,yieldThe command can only be followed by a Thunk function or a Promise object, andasyncFunction of theawaitThe command can be followed by a Promise object and a value of the primitive type.
  • The return value is Promise asyncThe function returns a Promise object, which is much more convenient than Generator functions that return an Iterator. You can usethenMethod specifies what to do next.

Async usage


Let’s start with a simple example of async:

function getProvinces () {
    return new Promise(resolve= > {
        setTimeout(resolve, 1000)})}async function asyncFn () {
    await getProvinces()
    console.log('hello async')}Copy the code

The above code first defines a getProvinces function to get the data, using setTimeout to simulate the asynchronous operation of the data request. When we use the keyword async before an asyncFn function, we indicate that there is an asynchronous operation within the function. When the await keyword is encountered, it waits for the asynchronous operation to complete before executing the following code. So the code will wait 1000 milliseconds before printing ‘Hello Async’ in the console.

Now that we know the basic usage of async, let’s understand how async works:

async function asyncFn1 () {
    return 'hello async'
}
asyncFn1().then(res= > {
    console.log(res)
})
// 'hello async'

async function asyncFn2 () {
    throw new Error('error')
}
asyncFn2().then(res= > {
    console.log(res)
}).catch(err= > {
    console.log(err)
})
// 'error'
Copy the code

Async returns a Promise object, and the value of the return becomes an argument to the then method callback if no error occurs. When an error is thrown, the Promise object changes to reject, and the error becomes an argument to the catch method callback.

async function asyncFn3 () {
    return await Promise.resolve('hello async')
}
asyncFn3().then(res= > console.log(res))
// 'hello async'

async function asyncFn4 () {
    return await 123
}
asyncFn3().then(res= > console.log(res))
/ / 123
Copy the code

Await (async wait) keyword if followed by a Promise object, returns the result of that Promise. If not, the value is returned as if resolve were executed immediately.

When async functions have more than one await function, we have to consider a case where a Promise state changes to Reject, because if any of the internal functions change to Reject, the subsequent functions will not be executed and the async function will change to Reject.

async function asyncFn5 () {
    await Promise.reject('error')
    return await Promise.resolve('hello async') // Will not be executed
}
asyncFn5().then(res= > {
    console.log(res)
}).catch(err= > {
    console.log(err)
})
Copy the code

In order to execute code correctly, we should await await error handling. There are two basic error handling methods:

async function asyncFn6 () {
    try {
        await Promise.reject('error')}catch (err) {
        console.log(err)
    }
    return await Promise.resolve('hello async')}// Use try... Catch

async function asyncFn7 () {
    await Promise.reject('error').catch(err= > console.log(err))
    return await Promise.resolve('hello async')}// A Promise object that can become reject is followed by a catch method to handle the error that occurred before
Copy the code

Now that you understand the basic usage of async/await, let’s use an example of a situation we often encounter at work to feel the magic of async/await.

Suppose we now want to get how many jurisdictions a prefecture-level city has, we now need to call the interface to get the local province, get the province ID from it to call the interface to get the prefecture-level city, get the ID of the prefecture-level city to get the final result.

function getProvinces () {... returnnew Promise(resolve= > {
        resolve(provinceId)
    }
}
function getCitys (provinceId) {... returnnew Promise(resolve= > {
        resolve(cityId)
    }
}
function getArea (cityId) {... returnnew Promise(resolve= > {
        resolve(areaData)
    }
}
Copy the code

If you implement a Promise like this:

getProvinces().then(provinceId= > getCitys(provinceId)).then(cityId= > getArea(cityId)
Copy the code

Async /await: async/await

async getData () {
    const provinceId = await getProvinces()
    const cityId = await getCitys(provinceId)
    return await getArea(cityId)
}

getData()
Copy the code

Although both methods can achieve our ultimate goal, in the case of more complex dependencies, using the Promise approach makes the chain very long and less readable than using async/await code.

Async Runs a process

The application of async in work is more diverse, because it seems to process asynchronous operations synchronously, solves the problem of constant callbacks, and increases the readability of code. Although async seems to be a synchronous operation, it is non-blocking. Next, async, Promise and setTimeout will be combined to deepen the understanding of async with a small example:

async function asyncFn1 () {
    console.log('asyncFn1 start')
    await asyncFn2()
    console.log('async1 end')}async function asyncFn2 () {
    console.log('asyncFn2')}console.log('script start')

setTimeout(function () {
    console.log('setTimeout')},0)

asyncFn1()

new Promise((resolve) = > {
    console.log('Promise')
    resolve()
}).then((a)= > {
    console.log('Promise.then')})console.log('script end')
Copy the code

The code above prints out eight statements, so take a moment to think about the order of execution.

The final printed result in the console is:

script start
asyncFn1 start
asyncFn2
Promise
script end
Promise.then
async1 end
setTimeout
Copy the code

There are probably a lot of people out there who got it right, but if your answer is slightly off, that’s fine. You can get a better idea of how asynchronous execution works. The code is executed in this order:

  1. Define an asynchronous asyncFn1 function
  2. Define an asynchronous asyncFn2 function
  3. Execute console.log(‘script start’) statement * 1
  4. Define a timer to output after 0ms (setTimeout is added to macroTasks queue, so execution priority is lower than that of microTasks queue)
  5. Execute asyncFn1:

Log (‘asyncFn1 start’) statement * 2

(2) Execute asyncFn2 * 3 when asyncFn1 is await

  1. Execute the Promise statement

Execute console.log(‘Promise’) statement * 4

(2) Resolve (), return a Promise object and add the Promise object to the MicroTasks queue

  1. Execute console.log(‘script end’) statement * 5
  2. The synchronization stack is complete
  3. Back in the asyncFn1 body, add the Promise object returned by the asyncFn2 function to the MicroTasks queue
  4. Take out the tasks in the MicroTasks queue and print console.log(‘ promise.then ‘) * 6
  5. Then execute the console.log(‘asyncFn1 end’) statement * 7 in the asyncFn1 body
  6. Finally, execute the tasks in the MacroTasks queue, console.log(‘setTimeout’) * 8

The above are some of my humble opinions on async/await knowledge. I write this article solely to consolidate my knowledge and hope it can also be of little help to readers.

This article is based on: ESMAScript6 starter and front-end ER. Can you really use Async?