你真的知道async/await吗?

94 阅读3分钟

前言

最近在公司项目开发中,遇到了一个bug,大概代码场景如下:

async function fun1() {
  await fun2();
  console.log("fun1");
}

async function fun2() {
  await fun3();
  console.log("fun2");
}

async function fun3() {
  new Promise((resolve, reject) => {
    // resolve("fun3");
  })
    .then(() => {
      console.log("第一个then");
    })
    .then(() => {
      console.log("第二个then");
    })
    .then(() => {
      console.log("第三个then");
    });
}

我的第一反应输出顺序是:

  1. 第一个then
  2. 第二个then
  3. 第三个then
  4. fun2
  5. fun1

然而,当我执行后发现实际输出结果是这样的

  1. 第一个then
  2. fun2
  3. 第二个then
  4. fun1
  5. 第三个then
248D075A.jpg

下面,我们开始从async/await的原理入手。

1、什么是async/await

async/await是一对好基友,缺一不可,服务于promise。有await的地方,其函数体必须使用async声明。例如:

let data = "data";
const demo = async function(){
    await data
}

如果下面这样声明就会报错:

let data = "data";
const async demo = function(){
    await data;
}
// 或者
let data = "data";
const async demo = function(){
    const test = function(){
        await data;
    }
}

因为:async声明的必须是一个function,其次await必须在async声明的直属函数使用。

2、async的本质

用一句话概括,async声明函数的本质其实就是返回一个Promise

例如:

let data = "data";
const demo = async function(){
    return data;
}
console.log(demo());
// Promise {<fulfilled>: 'data'}

//相当于
const demo = function(){
    return Promise.resolve("data");
    // 等同于
    // return new Promise((resolve,reject) => {
    //    resolve('data')
    // })
}
console.log(demo());
// Promise {<fulfilled>: 'data'}

//如何拿到返回值?
let data = "data";
const demo = async function(){
    return data;
}
demo().then((res) => {
  console.log(res); // data
});

也就是说,我们对待async的返回时要像对待Promise一样去对待。

3、await的本质

await,顾名思义,就是等待一会。而await的本质,就是可以提供等同于‘同步效果’的等待异步返回能力的语法糖。 例如:

const demo = async () => {
  let result = await new Promise((resolve, rej) => {
    setTimeout(() => {
      resolve("延迟一秒");
      console.log("一秒到了");
    }, 1000);
  });
  console.log("延迟一秒后执行");
};
demo().then((res) => {
  console.log(res);
});

你会发现输出顺序并不是你所想的一秒到了、延迟一秒后执行、延迟一秒,而是一秒到了、延迟一秒后执行、undefined,因为demo函数中没有return,所以then里面的res就是undefined,正确代码应该是:

const demo = async () => {
  let result = await new Promise((resolve, rej) => {
    setTimeout(() => {
      resolve("延迟一秒");
      console.log("一秒到了");
    }, 1000);
  });
  console.log("延迟一秒后执行");
  return result;
};
demo().then((res) => {
  console.log(res);
});
// 输出为 一秒到了  延迟一秒后执行  延迟一秒

更准确的说await声明其实是在等待一个Promise异步的返回,只有Promise有返回值的时候,代码才会继续往下执行。

再回到我们开始的代码:

async function fun1() {
  await fun2();
  console.log("fun1");
}

async function fun2() {
  await fun3();
  console.log("fun2");
}

async function fun3() {
  new Promise((resolve, reject) => {
    // resolve("fun3");
  })
    .then(() => {
      console.log("第一个then");
    })
    .then(() => {
      console.log("第二个then");
    })
    .then(() => {
      console.log("第三个then");
    });
}

因为async相当于返回一个Promise,而await是在等待等待异步返回。所以这段代码其实就相当于:

function fun1() {
  new Promise((resolve, reject) => {
    resolve();
  }).then(() => {
    new Promise((resolve, reject) => {
      resolve("fun3");
    })
      .then(() => {
        console.log("第一个then");
      })
      .then(() => {
        console.log("第二个then");
      })
      .then(() => {
        console.log("第三个then");
      });
  });
  new Promise((resolve, reject) => {
    resolve();
  }).then(() => {
    new Promise((resolve, reject) => {
      resolve();
    }).then(() => {
      console.log("fun2");
      new Promise((resolve, reject) => {
        resolve();
      }).then(() => {
        console.log("fun1");
      });
    });
  });
}
fun1();

到这里,我们通过event loop就可以判断出输出为:

  1. 第一个then
  2. fun2
  3. 第二个then
  4. fun1
  5. 第三个then