The most detailed explanation of async/await

My understanding:

The role of async is to define that this function is asynchronous: async is a keyword placed before the function, and in the function return 1, return "1", return new Promise(),

The function of external printing is obtained as a promise object, and the value of return is obtained through the function .then

The function of await is to wait  : if it is not a promise object it is waiting for, it will not block the following code, otherwise it will block the following code if it is waiting for a promise object, and it will directly get the data in resolve if it is decorated with await. This is the job of await

1. What async and await are doing

  • async is short for" asynchronous ", and await means to wait .
  • async is used to declare that a function is asynchronous , and await waits for an operation to complete .
  • async/await is a new way to write asynchronous code. The previous solutions for asynchronous code were callbacks and promises.

  • async/await, like promises, is non-blocking.

  • async/await makes asynchronous code look and behave more like synchronous code. This is where its power lies.

2. What does async do?

The key to this question is how the async function handles its return value!

Of course we hope that it can return the value we want directly through the return statement, but if this is the case, it seems that there is nothing to await. So, try writing some code and see what it returns:

<script>  
  async function test(){
    return 'hello async';
  }
  let result = test();
  console.log(result);
</script>

It suddenly dawned on me when I saw the output—the output is a Promise object.

Promise {<resolved>: "hello async"}

So, what the async function returns is a Promise object. The async function (including function statement, function expression, and Lambda expression) will return a Promise object. If a direct value is returned in the function, async will encapsulate the direct value into a Promise object through Promise.resolve().

 The async function returns a Promise object, so when the outermost layer cannot use await to get its return value, of course we should use the original way: then() chain to handle this Promise object, like this

async function test(){
   return 'hello async';
 }
 test().then((val) => {
   console.log(val);  //hello async 
 })

Thinking back now, what if the async function didn't return a value? It is easy to imagine that it will return Promise.resolve(undefined).

Think about the characteristics of Promise - no waiting, so if you execute an async function without await, it will execute immediately, return a Promise object, and will never block the following statements. This is no different from a normal function returning a Promise object.

Then the next key point is the await keyword.

3. What await is waiting for?

Because an async function returns a Promise object, await can be used to wait for the return value of an async function - it can also be said that await is waiting for the async function, but it should be clear that it is actually waiting for a return value. Note that await is not only used to wait for Promise objects, it can wait for the result of any expression, so, after await, it can actually be followed by ordinary function calls or direct quantities. So the following example works perfectly fine.

function getSomething(){
  return "something";
}
async function testAsync(){
  return Promise.resolve('hello async');
}
async function test(){
  let v1 = await getSomething();
  let v2 = await testAsync();
  console.log(v1,v2);
}
test();
console.log('我执行了');

//执行结果为:
//我执行了
//something,hello async

await waits for what to wait for, and then

await waits for what it is waiting for, a Promise object, or some other value, then what? I have to start by saying that await is an operator used to compose expressions, and the result of an await expression depends on what it is waiting for.

If it's not a Promise, the await expression evaluates to what it's waiting for.

If it waits for a Promise object, await will be busy. It will block the following code, wait for the Promise object to resolve, and then get the resolved value as the operation result of the await expression.

Seeing the word blocking above, I panicked... Don't worry, this is why await must be used in async functions. The async function call will not cause blocking (that is, the code on line 13 will not be blocked), and all internal blocking is encapsulated in a Promise object for asynchronous execution.

What does async/await do for us

make a simple comparison

It has been explained above that async will encapsulate the return value of the subsequent function (function expression or Lambda) into a Promise object, and await will wait for the Promise to complete and return the result of its resolution.

Now, for example, use setTimeout to simulate time-consuming asynchronous operations. First, let’s see how to write without async/await.

function takeLongTime(){
  return new Promise((resolve) => {
    setTimeout(() => resolve('long time value'),1000);
  })
}
takeLongTime().then((v) => {
  console.log('get:',v);
})

If you use async/await instead, it will be like this.

function takeLongTime(){
  return new Promise((resolve) => {
    setTimeout(() => resolve('long time value'),1000);
  })
}
async function test(){
  let v = await takeLongTime();//等待异步操作的结果,阻塞后面代码的执行
  console.log(v);
}

Sharp-eyed students have discovered that takeLongTime() is not declared as async. In fact, takeLongTime() itself is the returned Promise object, and the result is the same with or without async. If you don’t understand, please go back and look at the above "what does async do".

Another question arises. These two pieces of code, the difference between the two ways of processing asynchronous calls (actually the processing of Promise objects) is not obvious, and even using async/await requires more code, so what are its advantages? where?

The advantage of async/await is to handle the then chain

A single Promise chain cannot find the advantages of async/await, but if you need to deal with a then chain composed of multiple Promises, the advantages can be reflected (interestingly, Promise solves the problem of multi-layer callbacks through the then chain , and now use async/await to further optimize it).

Suppose a business is completed in multiple steps, each step is asynchronous and depends on the result of the previous step. We still use setTimeout to simulate asynchronous operations:

/*
 * 传入参数n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n+200,这个值将用于下一步骤
*/  
function takeLongTime(n){
  return new Promise((resolve) => {
    setTimeout(() => resolve(n + 200),n);
  })
}
function step1(n){
  console.log(`step1 with ${n}`);
  return takeLongTime(n);
}
function step2(n){
  console.log(`step2 with ${n}`);
  return takeLongTime(n);
}
function step3(n){
  console.log(`step3 with ${n}`);
  return takeLongTime(n);
}

Now use the Promise method to realize the processing of these three steps.

function doIt(){
  console.time('doIt');
  let time1 = 300;
  step1(time1)
    .then((time2) => step2(time2))
    .then((time3) => step3(time3))  
    .then((result) => {
      console.log(`result is ${result}`);
      console.timeEnd("doIt");
    })
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1510.2490234375ms

The output result is the parameter 700 + 200 = 900 of step3(). doIt() executes three steps in sequence, taking 300 + 500 + 700 = 1500 milliseconds in total, which is consistent with the result calculated by console.time()/console.timeEnd().

If it is implemented with async/await, it will be like this.

async function doIt() {
  console.time('doIt');
  let time1 = 300;
  let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
  let time3 = await step1(time2);
  let result = await step1(time3);
  console.log(`result is ${result}`);
  console.timeEnd('doIt');
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1512.904296875ms

The result is the same as the previous Promise implementation, but this code looks much cleaner, almost the same as the synchronous code.

there are even cooler

Now to change the business requirements, there are still three steps, but each step requires the results of each previous step.

/*
 * 传入参数n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n+200,这个值将用于下一步骤
*/  
function takeLongTime(n){
  return new Promise((resolve) => {
    setTimeout(() => resolve(n + 200),n);
  })
}
function step1(n){
  console.log(`step1 with ${n}`);
  return takeLongTime(n);
}
function step2(m,n){
  console.log(`step2 with ${m} + ${n}`);
  return takeLongTime(m + n);
}
function step3(k,m,n){
  console.log(`step3 with ${k} + ${m} + ${n}`);
  return takeLongTime(k + m + n);
}

This time, use async/await to write:

async function doIt() {
  console.time('doIt');
  let time1 = 300;
  let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
  let time3 = await step2(time2,time1);
  let result = await step3(time3,time2,time1);
  console.log(`result is ${result}`);
  console.timeEnd('doIt');
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500 + 300
//step3 with 1000 + 500 + 300
//result is 2000
//doIt: 2916.655029296875ms

Except that the execution time has become longer, it seems to be the same as the previous example! Don't worry, think carefully about what it would look like if it were implemented as a Promise.

function doIt() {
  console.time('doIt');
  let time1 = 300;
  step1(time1)
    .then((time2) => {
      return step2(time1,time2)
          .then((time3) => [time1,time2,time3])//step3需要用到time1,time2,time3,因此需要返回
    })
    .then((times) => {
      let [time1,time2,time3] = times;
      return step3(time1,time2,time3)
    })
    .then((result) => {
      console.log(`result is ${result}`);
      console.timeEnd('doIt');
    })
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 300 + 500
//step3 with 300 + 500 + 1000
//result is 2000
//doIt: 2919.49609375ms

 Does it feel a little complicated? That bunch of parameter processing is the Achilles' heel of the Promise solution—parameter passing is too troublesome, and it makes me dizzy just looking at it!

Points
to note For now, have you understood async/await? But in fact, there are still some things not mentioned - Promise may reject, how to deal with it?

The Promise object behind the await command may be rejected, so it is better to put the await command in the try...catch code block.

async function myFunction() {
  try {
    await somethingThatReturnAPromise();
  } catch (err){
    console.log(err);
  }
}

//另一种写法
async function myFunction() {
  await somethingThatReturnAPromise().catch(function(err) {
    console.log(err);
  })
}

Guess you like

Origin blog.csdn.net/qq_52421092/article/details/130333581