本文的执行过程乃个人的理解,只求单纯把题目给做出来,并未涉及到原理的分析。
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
graph TD
print4 --> print2
由此可知,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