await和async执行流程理解

486 阅读4分钟

本文的执行过程乃个人的理解,只求单纯把题目给做出来,并未涉及到原理的分析。

async函数返回的是什么?

返回的是一个Promise对象。它的返回规律和Promise的then函数的返回规律大同小异

  • 情况一:返回一个非Promise(如果什么都不返回,js函数就会默认return undefined)
    async function(){
        return v;
        //v为非Promise值
    }
    //等效于
    function(){
        return new Promise((resolve,reject)=>{resolve(v)});
        //返回一个resolved状态,值为v的Promise
    }
    
  • 情况二:返回一个Promise
    async function(){
        return new Promise((resolve,reject)=>{resolve(1)});
        //v为非Promise值
    }
    //等效于
    function(){
        return new Promise((resolve,reject)=>{resolve(1)});
        // 返回的Promise的状态和值均与原返回的Promise相同
    }
    
  • 情况三:throw一个值
    async function(){
        throw e;
        //v为非Promise值
    }
    //等效于
    function(){
        return new Promise((resolve,reject)=>{reject(e)});
        //返回一个rejected状态,值为e的Promise
    }
    

await干了什么

  • await会将当前的async函数的上下文挂起,等待await表达式的结果
  • await会将其之后的表达式转化为Promise对象,以它为界,函数体之后的代码被推入then()函数内
/* 1、await一个非Promise对象 */
    async function f(){
        // v 为非Promise
        await v;
        console.log('nia nia');
    }
    // 等效于
    async function f(){
        new Promise((resolve)=>{resolve(v)}).then((res)=>{console.log('nia_nia')});
        // 如果v是一个函数,将会执行函数的逻辑,并将函数的return值作为v值
    }
    
/*2、await一个Promise对象*/    
   async function f(){
        // v 为Promise 对象
        await new Promise((resolve)=>{setTimeout(()=>{resolve()},1000)});
        console.log('nia nia');
    }
    // 等效于
    async function f(){
        new Promise((resolve)=>{setTimeout(()=>{resolve()},1000)}).then(
        ()=>{console.log('nia nia')});
    }
    
    
    async function f(){
        // v 为Promise 对象
        await new Promise((resolve)=>{setTimeout(()=>{resolve()},1000)});
        console.log('nia nia');
    }
    // 等效于
    async function f(){
        new Promise((resolve)=>{setTimeout(()=>{resolve()},1000)}).then(
        ()=>{console.log('nia nia')});
    } 
    

then的执行时机

当将aysnc和await代码用Promise等效语句替代完,面临的就是执行时机的问题了。
首先,Promise函数体内的代码是同步执行的;而then中的代码属于微任务,需要被加入微任务队列中执行。而then中代码被加入微任务队列的前提就是Promise对象已经从pending转变成了准确的状态。

new Promise((r) => {console('p1') ;r(); }) 
.then(() => console.log(1))
.then(() => console.log(2)) 
.then(() => console.log(3))

new Promise((r) => { console('p2');r(); }) 
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))

在以上代码中:

  • 两个Promise当中的代码为同步代码。因此输出p1后,第一个Promise 调用r()变为了resolved状态,从而将第一个then:log(1)推入了微任务队列
  • 这时开始执行第二个Promise中的代码。因此输出p2后,第二个Promise 调用r()变为了resolved状态,从而将第一个then:log(4)推入了微任务队列,此时微任务队列如下
  • graph TD
    print1 --> print4
    
  • 此时同步代码执行完成,从微任务队列中取出队首,即then:log(1),then:log(1)的执行返回一个状态为resolved的Promise(状态确定),从而then:log(1)的下一个链结then:log(2)被推入了微任务队列
  • graph TD
    print4 --> print2
    
  • 以此类推,取出then:log(4)后,会将then:log(3)挂上队列
  • ... .........
  • result: p1 ,p2 ,1, 4, 2, 5 ,3, 6

由此可知,await后面的代码同理,主要看await所返回的Promise的对象何时会有一个确切的状态,并且当得到这个状态时,将then中的代码推入到微任务队列中

例子2

console.log(1);
async function async1(){ 
    console.log(2); 
    await new Promise((resolve)=>{setTimeout(()=>{console.log('timeout');resolve(1)},2000);});
    setTimeout(()=>{console.log('timeout2');},1000);
};

async1();
console.log(5);
  • 输出1.开头,同步代码
  • 输出2.进入async1,开头,为同步代码
  • await Promise中的计时器为异步代码,加入到宏任务队列,继续向下执行。此时Promise为pending状态,then中代码无法推入微任务队列
      await new Promise((resolve)=>{setTimeout(()=>{console.log('timeout');resolve(1)},2000);});
      setTimeout(()=>{console.log('timeout2');},1000);
      
      // 等效于
      new Promise((resolve)=>{setTimeout(()=>{console.log('timeout');resolve(1)},2000);})
          .then(()=>{setTimeout(()=>{console.log('timeout2');},1000);})
    
  • 输出5.同步代码
  • 开始执行异步任务,计时器等待2秒,执行输出timeout,并resolve(1).此时Promise状态变化,将then后代码推入到微任务队列中。
  • 执行微任务,经过1秒,输出timeout2
  • result:1 2 5 timeout timeout2

例子3

console.log(1);
function fn1(){
    function fn2(){
        async function async1(){ 
            console.log(2);
            await fn3();
            console.log(3);
        }

        function fn3(){ console.log(4); }
        await async1();
        new Promise(resolve => resolve(5)).then(res => console.log(res));
        console.log(6);
    }
    fn2();
    console.log(7);
}
fn1();
console.log(8);
// 1 2 4 7 8 3 6 5

  • 输出1.log(1)
  • 输出2.进入async1,log(2)
  • 输出4.进入fn3,log(4),因为fn3返回一个状态确定的Promise,因此then:log(3)推入微任务队列
  • 输出7.同步代码
  • 输出8.同步代码
  • 同步代码执行结束。查看微任务队列,执行log(3)。
  • async1()状态确定将之后的代码推入微任务队列。
  • 执行。new Promise()状态确定,将log(5),推入微任务队列
  • 输出6.同步代码
  • 查看微任务队列。log(5),输出5