Please indicate the source of reprint:

Async function parsing – Digging gold

Async function parsing – Blog garden

Async function parsing – Zhihu

Async functions are implemented based on Generator functions, which is the syntactic sugar of Generator functions. Generator syntax and asynchronous applications have been introduced in previous articles. If you are not familiar with Generator syntax, you can read the article on Generator functions first so that learning async functions is not too difficult.

Portal: Generator syntax parses Generator functions for asynchronous applications

Next, async functions will be explained in some length, and reference learning articles of async functions will be given at the end of the paper.


The article directories

  1. meaning
  2. The basic grammar
  3. Error handling
  4. Asynchronous application

meaning

As we know, calling the Generator function does not execute immediately, but returns the traverser object. Tired of manually executing traverser objects, the thunk(Thunkify) function combined with the RUN function implements automatic process management. Alternatively, the CO module can be used for automatic process management, making the use of Generator functions more convenient.

The ES2017 standard introduces the syntax of async functions, which is the syntax-sugar of Generator functions. Therefore, compared with Generator functions, async functions have the following basic features.

Built-in actuators: Async functions can be called and executed just like normal functions. Do not use the CO module for process control as Generator functions.

More semantic: the async keyword means that it is an asynchronous function, and await means that it needs to be executed. More semantic than yield expressions.

Return a Promise: Async functions return a Promise object, which is much more convenient than Generator functions that return an Iterator. You can use the then method to specify the next action.

The basic grammar

Use the async keyword to indicate that a function is an async function, and use the await keyword internally to indicate that the asynchronous task needs to finish before continuing.

async function as () {
  return 123
}
as().then(data => {
  console.log(data)
})
Copy the code

As you can see from the above code, calling async returns a Promise object that can be used as the parameter value for the successful processing of the function by the then method.

If an error or exception is thrown inside async, it will be caught by the then method’s error handler or catch method.

async function as () {
  throw new Error('Wrong pull! ')} as().then(data => {console.log(data)}).catch(err => {console.log(err)}) // Error: xixi, catch method caught an ErrorCopy the code

In addition, async functions can use the await keyword inside to indicate that the following expression is an asynchronous task. The expression following the await keyword can be a Promise object or a simple (complex) data type (Number, String, RegExp, Boolean, Array, Objext). If it is a simple (complex) data type, the async function implicitly calls the promise.resolve method to convert it to a Pormise object.

function foo () {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      resolve('async')
    }, 1000)
  })
}
async function as() {const data = await foo() //foo is usedsetTimeout to simulate asynchrony. console.log(data) } as() // async asyncfunction as () {
  returnAwait 123 // Same if other data type. } as().then(data => { console.log(data) }) // 123Copy the code

If the expression following the await keyword is a non-promise, non-thenable plain value, then the Promise. Resolve method is implicitly called to convert it to a Promise object, and the await keyword internally calls the THEN method to return the resolve value.

The internal implementation of await is roughly as followsfunction await (data) {
  return new Promise((resolve, reject) => {
    resolve(data)
  }).then(data => {
    return data
  })
}
Copy the code

In short, the await keyword is the syntactic sugar of the then method, passing the value of resolve.

In addition, if an expression after the await keyword throws an error, the async function returns a Promise object from a pending state to a Reject state, which is then caught by the catch method.

function foo () {
  throw new Error('err')
}
async function as() { await foo() } as().then(data => {}) .catch(err => { console.log(err); }) // The Promise returned by as changes from pending to reject.Copy the code

If an expression following an await keyword throws an error, the async function changes to reject, the function is suspended, and subsequent expressions are no longer executed. Because a feature of the Promise function is that once the state changes, it never changes again, and it stays the same state when called.

function foo () {
  throw new Error('err')
}
async function as () {
  await foo()
  return Promise.resolve('succ') // It will not be executed because the state of the Promise object will never change once it changes, so it will not be executed. } as().then(data => {}) .catch(err => { console.log(err); })Copy the code

Because async functions return Promise objects by default, async functions can be used as expressions after the await keyword. It is more convenient for an async function to call another async function, unlike Generator functions that require yield* expressions.

async function foo () {
  return Promise.resolve('async')
}
async function as () {
  return} as().then(data => {console.log(data)})Copy the code

In addition, if no errors are thrown inside an async function, the function executes normally. Then the asynchronous task after each await keyword will be subsequently executed. That is, one asynchronous task is completed before another asynchronous task is executed, not concurrently.

async function foo () {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      resolve(10)
    }, 1000)
  })
}
async function bar () {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      resolve(20)
    }, 2000)
  })
}
async function as () {
  let t1 = Date.now()
  const a = await foo()
  const b = await bar()
  letT2 = date.now () console.log(t2-t1) // Error: 3004msreturnA + b} as().then(data => {console.log(data) // about 3s enter 30})Copy the code

If two asynchronous tasks do not depend on each other, the downside of this is time wasted if the code above is followed by two asynchronous tasks. Two asynchronous tasks that could have been completed in 200ms took 400ms. So you can have two asynchronous tasks that are not dependent on each other firing at the same time. There are two ways:

// Asyncfunction as() {const t1 = date.now () const [fo, ba] = [foo(), bar()] Const a = await fo const b = await ba const t2 = date.now () console.log(T2-t1)returnAll waits for all asynchronous tasks to complete before returning asyncfunction as () {
  const t1 = Date.now()
  const arr = await Promise.all([foo(), bar()])
  const t2 = Date.now()
  console.log(t2 - t1)
  return arr[0] + arr[1]
}
as().then(data => {
  console.log(data)  // 30
})
Copy the code

Error handling

Once an error occurs in an asynchronous task within an async function, the Promise object returned by an async function is rejected. Therefore, to prevent errors in asynchronous tasks, try… Catch to catch errors so that async functions can be executed internally.

async function as () {
  let a = 0
  let b = 0
  try {
    a = await foo()
    b = await bar()
  } catch (e) {}
  return a + b
}
as().then(data => {
  console.log(data) // 30
})
Copy the code

As we know, try… Catch can only be used to handle synchronous operations and cannot catch errors for asynchronous tasks. The await keyword suspends function processing and waits for the asynchronous task to return. So use try… in async functions. Catch combined with the await keyword is a good way to catch asynchronous errors.

Asynchronous application

Let’s look at the differences between implementing asynchronous applications using Promise, Generator, and Async. Next, setTimeout is used to simulate asynchrony.

Let’s start with two basic functions

function foo (obj) {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      let data = {
        height: 180
      }
      data = Object.assign({}, obj, data)
      resolve(data)
    }, 1000)
  })
}
function bar (obj) {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      let data = {
        talk () {
          console.log(this.name, this.height);
        }
      }
      data = Object.assign({}, obj, data)
      resolve(data)
    }, 1500)
  })
}
Copy the code

Both functions internally return the Promise instance Object, which is used to merge the passed parameters with object. assign.

Let’s start with the implementation of pure Promise objects.

function main () {
  return new Promise((resolve, reject) => {
    const data = {
      name: 'keith'
    }
    resolve(data)
  })
}
main().then(data => {
  foo(data).then(res => {
    bar(res).then(data => {
      return data.talk()   // keith 180
    })
  })
})
Copy the code

The then method is constantly used in the call process, which is not intuitive, and the semantics of the operation itself are not easy to see. And there is a risk of a correction hell.

Let’s look at the implementation of the Generator function. Since the invocation of the Generator function needs to be performed manually, the run function is written to implement automatic process control.

function *gen () {
  const data = {
    name: 'keith'
  }
  const fooData = yield foo(data)
  const barData = yield bar(fooData)
  return barData.talk()
}
function run (gen) {
  const g = gen()
  const next = data => {
    let result = g.next(data)
    if (result.done) return result.value
    result.value.then(data => {
      next(data)
    })
  }
  next()
}
run(gen)
Copy the code

Using the RUN function for automatic flow control, Generator functions have the advantage over Promise objects of synchronizing asynchronous processes with less risk of callback hell. However, the disadvantage is that you need to use functions like run or CO modules to implement flow control.

Let’s use async to do this.

async function main () {
  const data = {
    name: 'keith'
  }
  const fooData = await foo(data)
  const barData = await bar(fooData)
  return barData
}
main().then(data => {
  data.talk()
})
Copy the code

From the above code, it can be seen that the amount of code using async function is the least and synchronizes the asynchronous process. Furthermore, async function has built-in executor. The method called is more concise.


Ok, so much for async functions, just to summarize.

  1. Async functions are implemented based on Generator functions and are syntactic sugar of Generator functions. It has a built-in executor that returns a Promise object after being called, so it can be used like ordinary Korean.
  2. An error thrown inside an async function or an error thrown by an expression following the await keyword causes the Promise object returned by an async function to return frompendingState changes torejectState so that errors can be caught by the catch method. Furthermore, once the state of the Promise object changes, it does not change, and subsequent asynchronous tasks are not executed.
  3. The expression following the await keyword can be a Promise object or another data type. If it is any other data type, it will passPromise.resolveConvert it to a Promise object
  4. If there are multiple await keywords inside an async function, subsequent asynchronous tasks will be executed. This can be used if each asynchronous task does not depend on each otherPromise.allMake it run concurrently so that multiple asynchronous tasks can be completed in the same time, increasing the efficiency of function execution.
  5. For errors thrown internally by async, usetry... catchTo catch exceptions. althoughtry... catchIt can only be used to capture synchronous tasks, but the await keyword synchronizes asynchronous tasks and therefore can be combinedtry... catchAnd await keywords to capture asynchronous tasks.

References:

  1. Async function
  2. The meaning and usage of async functions