When you are working on a project, you often run into the problem of asynchrony. You have to control the order of multiple asynchrony requests. What should you do? How to deal with callback hell involving multiple callbacks? The ES2017 standard introduces async functions to make asynchronous operations more convenient. This article mainly introduces the basic usage of async/await, parallel tasks and matters needing attention.

0. Traditional JS asynchronous programming method

Before ES6 came out, there were basically four methods of asynchronous programming.

  • The callback function
  • Event listeners
  • Publish/subscribe
  • Promise object

1. Basic usage

First of all, we use async/await and traditional asynchronous method respectively to write through an example, and then have a preliminary understanding of async/await writing method.

Define a Fetch method to Fetch information:

function fetchUser(){
    return new Promise((resolve, reject) = > {
        fetch('https://whuzxq.com/userList/4356')
        .then((data) = >{ resolve(data.json()); }, (error) => { reject(error); })}); }Copy the code

Promise way

function getuserByPromise(){
    fetchUser()
        .then((data) = > {
            console.log(data);
        }, (error) => {
            console.log(error); })}Copy the code

Promise’s approach solved the callback hell, but the whole code was full of THEN, semantic was not obvious, and the code flow could not well represent the execution flow.

Async mode

/**
 * async 方式
 */
 async function getUserByAsync(){
     let user = await fetchUser();
     return user;
 }
getUserByAsync()
.then(v= > console.log(v));
Copy the code

The async function perfectly solves the above two problems. At the same time async function has its own executor, no need to manually load during execution.

Through the above two examples, we should have a preliminary understanding of the use of async. Important knowledge points related to async/await will be listed in detail below.

async

1. Async function returns a Promise object.

2. The value returned by the return statement inside the async function becomes the parameter of the callback function of the then method.

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

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

3. Promise objects returned by async functions will not change state until all Promise objects following the internal await command have been executed, unless a return statement or an error is thrown. That is, the callback function specified by the THEN method is executed only after the asynchronous operation inside the async function is completed.

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

In the above code, the function getTitle has three internal operations: fetch the page, retrieve the text, and match the page title. The console.log in the then method is executed only when all three operations are complete.

Await orders

The following points need to be understood: 1. 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

2. 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'); // Will not be executed
}
Copy the code

Sometimes we want to not interrupt subsequent asynchronous operations even if the previous one fails. We can then put the first await in a try… Inside the catch structure, so that the second await is executed regardless of whether the asynchronous operation succeeds or not.

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 a Promise object followed by a catch method 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))
/ / make a mistake
// hello world
Copy the code

Matters needing attention

1) await a Promise object (rejected), so it is best to await it in a try… In the catch block.

2. Await command can only be used in async functions, if used in normal functions, an error will be reported.

2. Wait for parallel tasks

When you do asynchronous JavaScript programming, you often need to code multiple complex statements one by one and label “await” them before calling them. Because most of the time, a statement doesn’t depend on a previous statement, but you still have to wait for the previous statement to complete, this can cause performance problems. Therefore, asynchronous operations following multiple await commands are best fired at the same time if there is no secondary relationship.

let foo = await getFoo();
let bar = await getBar();
Copy the code

In the code above, getFoo and getBar are two independent asynchronous (that is, non-dependent) operations written as secondary. This is time consuming because getBar will not be executed until getFoo is done, and they can be fired at the same time.

/ / write one
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

/ / write two
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

Copy the code

Example 3.

Example 1: Suppose a series of animations are deployed on a DOM element, and the first animation ends before the next one begins. If one of the animations fails, it does not proceed further and returns the return value of the last successfully executed animation.

async function chainAnimationsAsync(elem, animations) {
  let ret = null;// The ret variable is used to hold the return value of the previous animation
  try {
    for(let anim of animations) {
      ret = awaitanim(elem); }}catch(e) {
    /* Ignore the error and continue with */
  }
  return ret;
}
Copy the code

Example 2: In real development, you often encounter a set of asynchronous operations that need to be completed in sequence. For example, read a set of urls remotely in turn and output the results in the order they were read.

async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(awaitresponse.text()); }}Copy the code

The above code is really much simpler, but the problem is that all remote operations are secondary. Only when the previous URL returns a result will the next URL be read, which is inefficient and a waste of time. What we need is to make remote requests concurrently.

async function logInOrder(urls) {
  // Read the remote URL concurrently
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // Output in order
  for (const textPromise of textPromises) {
    console.log(awaittextPromise); }}Copy the code

In the above code, although the map method takes an async function as an argument, it is executed concurrently, because only the async function is executed internally, and the outside is not affected. Behind the for… The of loop uses await inside, so it outputs sequentially.

4. Refer to web sites

http://es6.ruanyifeng.com/#docs/async https://github.com/xitu/gold-miner/pull/3738/files https://juejin.cn/post/6844903487805849613 https://juejin.cn/post/6844903597629505544

The original article can be found on my blog