前言
最近在公司项目开发中,遇到了一个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");
});
}
我的第一反应输出顺序是:
- 第一个then
- 第二个then
- 第三个then
- fun2
- fun1
然而,当我执行后发现实际输出结果是这样的
- 第一个then
- fun2
- 第二个then
- fun1
- 第三个then
下面,我们开始从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就可以判断出输出为:
- 第一个then
- fun2
- 第二个then
- fun1
- 第三个then