Today, we will talk about the async function in ES6. When we understand a concept, there are three aspects

  • What is the
  • why
  • How to use

If you feel that the article is too long, you can directly pull to the following, see the summary 🙄

What is the sync

ES7 provides async functions to facilitate asynchronous operations. What is async function? In short, async functions are syntactic sugar for Generator functions.

Let’s do an example, fetch and read files

The Generator function

var fs = require('fs');

var readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) reject(error);
      resolve(data);
    });
  });
};

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
Copy the code

I’m going to write async as a function, which looks like this.

var asyncReadFile = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
Copy the code

A comparison shows that an async function simply replaces the asterisk (*) of a Generator function with async, yields with await, and nothing more.

Why async when you have a Generator

Why async functions

The improvements of async over Generator are shown in the following four aspects.

(1) Built-in actuators. Generator functions must be executed by an executor, hence the CO module, while async functions have their own executor. In other words, async functions are executed exactly like normal functions, with only one line.

var result = asyncReadFile();
Copy the code

The above code calls the asyncReadFile function, which then automatically executes and outputs the final result. This is not at all like a Generator function, where you need to call the next method or use the CO module to actually execute and get the final result.

(2) Better semantics. Async and await are semantic clearer than asterisks and yield. Async means that there is an asynchronous operation in a function, and await means that the following expression needs to wait for the result.

(3) wider applicability. The CO module convention is that yield can only be followed by Thunk functions or Promise objects, while async functions can be followed by await commands with Promise objects and primitive values (numeric, string, and Boolean values, but this is equivalent to synchronous operations).

(4) Return the Promise. Async functions return a Promise object, which is much more convenient than Generator functions returning an Iterator. You can specify what to do next using the then method.

Further, async functions can be thought of as multiple asynchronous operations wrapped as a Promise object, and await commands are syntactic sugar for internal THEN commands.

grammar

How to use async?

A function can be preceded by async to make the number of functions asynchronous, out of the original execution order

console.log(1)
async function asyfun() { console.log(2) } asyfun(); Console. log(3) // prints the result: 1,3,2Copy the code

(1) Async returns a Promise object.

The value returned by the return statement inside the async function becomes an argument to the then method callback function.

async function f() {
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"
Copy the code

In the above code, the value returned by the return command inside f is received by the then callback.

An error is thrown inside the async function, which causes the returned Promise object to become reject. The thrown error object is received by the catch method callback.

async function f() {
  throw new Error('Wrong'); } f().then(v => console.log(v), e => console.log(e)) // Error: Error occurredCopy the code

(2) The state of the Promise returned by the async function will not change until all the Promise objects of the internal await command have been executed. That is, the callback function specified by the THEN method is executed only after the asynchronous operation inside the async function is completed.

Here’s an example.

async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"

Copy the code

(3) Normally, an await command is followed by a Promise object. If not, it is cast into an immediately resolve Promise object.

async function f() {
  return await 123;
}

f().then(v => console.log(v))
// 123
Copy the code

In the code above, the argument to the await command is the value 123, which is converted to a Promise object and resolved immediately.

If the Promise object following the await command becomes reject, the reject argument is received by the catch callback.

async function f() {
  await Promise.reject('Wrong'); } f (). Then (v = > console. The log (v)). The catch (e = > console. The log (e)) / / wrongCopy the code

Notice that the await statement is preceded by no return, but the reject argument is still passed to the catch callback. Here the effect is the same if return is preceded by await.

As soon as the Promise after an await statement changes to reject, the entire async function is interrupted.

async function f() {
  await Promise.reject('Wrong');
  await Promise.resolve('hello world'); // do not execute}Copy the code

In the above code, the second await statement will not be executed because the state of the first await statement changes to reject.

To avoid this problem, we can put the first await in the try… Inside the catch structure, so that the second await is executed.

async function f() {
  try {
    await Promise.reject('Wrong');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world

Copy the code

The alternative is to await the Promise object followed by a catch aspect to handle any errors that may occur earlier.

async function f() {
  await Promise.reject('Wrong')
    .catch(e => console.log(e));
  return await Promise.resolve('hello world'); } f().then(v => console.log(v)Copy the code

If you have more than one await command, you can put the try… Catch structure.

async function main() {
  try {
    var val1 = await firstStep();
    var val2 = await secondStep(val1);
    var val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3); } catch (err) { console.error(err); }}Copy the code

(4) If an asynchronous operation following await fails, the Promise object returned by the async function is rejected.

async function f() {
  await new Promise(function (resolve, reject) {
    throw new Error('Wrong'); }); } f().then(v => console.log(v)).catch(e => console.log(e)) // Error: an Error occurredCopy the code

In the above code, after async f is executed, the Promise object behind await will throw an error object, causing the callback of the catch method to be called with the argument of the thrown error object. For specific execution mechanism, please refer to “Implementation of Async Function” later.

The way to prevent errors is to put it in a try… Inside the catch block.

async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('Wrong');
    });
  } catch(e) {
  }
  return await('hello world');
}
Copy the code

summary

  1. Async is better than GenerStor, syntactically more semantic, and has built-in actuators
  2. The async return value is a Promise, and the return value inside the function becomes an argument to the then method callback.
  3. Await can only be used inside async functions
  4. If an asynchronous operation following await fails, the Promise object returned by the async function is rejected

This article was first published on wechat official number: Node front-end