async and await are actually syntax sugars of generator and promise

Async keyword is used to declare asynchronous functions. await is used to change asynchronous code into synchronization in async functions and block code execution

For those unfamiliar with promise and generator, you can take a step-by-step look at these articles

Understanding and use of Promise (I)
Understanding and use of Promise (II)
Step by step analysis of handwritten promise
javascript event cycle mechanism and detailed explanation of interview questions

Derivation of async and await

We introduce the function of async await keyword with a case.
The asynchronous function getRequestData is used to wait 2s for the input function to return.

function getRequestData(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(data);
    }, 2000);
  });
}

Call the function 3 times in succession, pass in "aaa" and splice "BBB" and "CCC" successively. The final result is to wait for 6s to output "aaabbbccc".

Callback hell implementation
getRequestData("aaa").then((res) => {
  getRequestData(res + "bbb").then((res) => {
    getRequestData(res + "ccc").then((res) => {
        console.log("result", res);
    });
  });
});

Callback hellish code has poor readability and maintainability. To solve this problem, we try chain call.

call chaining
getRequestData("aaa")
  .then((res) => {
    return getRequestData(res + "bbb");
  })
  .then((res) => {
    return getRequestData(res + "ccc");
  })
  .then((res) => {
    console.log("res", res);
  });

The readability of the code is slightly stronger through chain call, but such code is still not easy to understand. Let's optimize it

generator
function* showData() {
  const res1 = yield getRequestData("aaa");
  const res2 = yield getRequestData(res1 + "bbb");
  const res3 = yield getRequestData(res2 + "ccc");
  console.log("res", res3);
}

const generator = showData();
generator.next().value.then(res=>{
  generator.next(res).value.then(res=>{
    generator.next(res).value.then(res=>{
      console.log(res)
    })
  })
})

By passing the value of the function through yield, the code for requesting in the generator function becomes simpler, but the next call seems to be more complex. There are still functions nested, but calling the next method has traces to follow and can be implemented by recursion.

const generator = showData();
function execGenerator(generator){
  function exec(res){
    const gen = generator.next(res)
    if(gen.done){
      return gen.value
    }
    return gen.value.then(re=>{
      exec(re)
    })
  }
  exec()
}
execGenerator(generator)

After encapsulating the next method call, the asynchronous request becomes like synchronous code. You can wait for the previous function to execute successfully before calling the next function, which makes the code more readable.

aysnc,await

The final async and await codes are as follows. In fact, they are the syntax sugar of generator and promise, eliminating the recursive process of the next method.

async function showData(){
  const res1 = await getRequestData("aaa");
  const res2 = await getRequestData(res1 + "bbb");
  const res3 = await getRequestData(res2 + "ccc");
  console.log("res", res3);
}
showData()

The difference between async function and ordinary function

Return value

async functions can also have return values. Like promise, its return values are divided into three types

  • The return value is the basic data type and will be promise Resolve the package and execute a successful callback
  • The return value is promise, and the execution result is consistent with this promise
  • The return value is the object that implements thenable, and the execution result is the result in the then function
async function foo() {
  // return 'hello'
  
  // return new Promise((resolve, reject)=>{
  //   reject('fail')
  // })
  
  return {
    then: (resolve, reject) => {
      reject("fail");
    },
  };
}

const promise = foo();
promise
  .then((res) => {
    console.log("res:", res);
  })
  .catch((err) => {
    console.log("err:", err);
  });

Reject is returned in thenable, so the result of the asynchronous function is also reject, and the logic of the catch function is executed

Throw exception

Exceptions will be thrown in the ordinary program, and will not be executed directly

function bar(){
  throw new Error('error')
}
bar()
console.log('The above code threw an exception')

If an exception is thrown in an asynchronous function, you only need to define the catch method, which will not interrupt the program execution

async function foo() {
  throw new Error('error')
}
const promise = foo();
promise
  .then((res) => {
    console.log("res:", res);
  })
  .catch((err) => {
    console.log("Entered catch method");
  });
console.log('The above code threw an exception')

async other considerations

Asynchronous function execution reject

promise may return resolve or reject. When reject is executed, catch method needs to be defined when async function is executed

function getData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("fail");
    }, 1000);
  });
}
async function foo() {
  const promise = await getData();
  console.log(promise);
}
foo()

When the catch method is not defined, an error will be reported directly

await execution results

await will be followed by an expression, which will return promise. await will wait for promise to execute resolve before continuing to execute later.
There are also three cases of expressions after await, which are consistent with the return value type of async function

  • The expression is a basic data type and will be promise The resolve package will execute successfully
  • The expression is promise, and the execution result is consistent with the promise
  • The expression is an object that implements thenable, and the execution result is the result in the then function
async function foo() {
  await Promise.reject("promise Yes reject method");
  console.log("await Will the following line of code be executed");
}
foo().catch((err) => {
  console.log("err", err);
});
console.log("What about the global code");

When the expression after await fails to execute, the following code will not execute

Execution order of async functions

The code below await needs to wait until the expression after await is executed

async function bar() {
  console.log("2");
  return new Promise((resolve) => {
    resolve();
  });
}
async function foo() {
  console.log("1");
  await bar();
  console.log("3");
}
foo();
console.log("4");

The expression after await will actually execute promise, so the code below await will actually be put into the micro task

Interview questions

async function async1 () {
  console.log('async1 start')
  await async2();
  console.log('async1 end')
}

async function async2 () {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)
 
async1();
 
new Promise (function (resolve) {
  console.log('promise1')
  resolve();
}).then (function () {
  console.log('promise2')
})
console.log('script end')
  • The functions async1 and async2 are defined, but they are not called. First, output "script start"
  • The contents in setTimeout will be put into the macro task queue and finally executed
  • Call function async1 and output "async1 start". Execute function async2 and output "async2". The output "async1 end" under function async2 will be placed in the micro task queue
  • Execute the executor part of promise and output "promise 1". The output "promise 2" in the then method will be put into the micro task queue
  • Output "script end"
  • Execute the code in the micro task queue and output "async1 end" and "promise2" successively
  • Execute the code in the macro task queue and output "setTimeout"

The illustration is as follows

The results are as follows

The above is the derivation process and usage of async and await. There are still many places for developers to master about js advanced. You can see other blog posts I wrote, which are constantly updated~

Keywords: Javascript

Added by cbesh2 on Sun, 02 Jan 2022 06:27:33 +0200