阅读 79

彻底搞懂async/await和promise的执行顺序

前言

看了网上很多关于async/await和promise执行顺序的文章,感觉都没有说到点子上,都是根据结果在推测执行顺序。本文带你彻底搞懂这两者的执行顺序。前提是你对async/await和promise本身就已经有了一定的了解。

例题

首先看一下下面这几道例题,思考一下每题的输出结果。

// 例子1
console.log("-------例子1-------");
const p1 = Promise.resolve()
async function foo() {
  await p1;
  console.log('await');
}
foo()
p1.then(()=>{
  console.log('promise');
})


// 例子2
console.log("-------例子2-------");
const p2 = Promise.resolve()
async function foo() {
  await p2;
  console.log('await');
}
p2.then(()=>{
  console.log('promise');
})
foo()


// 例子3
console.log("-------例子3-------");
const p3 = Promise.resolve()
async function foo() {
  await 1;
  console.log('await');
}
foo()
p3.then(()=>{
  console.log('promise');
})


// 例子4
console.log("-------例子4-------");
const p4 = Promise.resolve()
async function foo() {
  await 1;
  console.log('await');
}
p4.then(()=>{
  console.log('promise');
})
foo()


 // 例子5
 console.log("-------例子5-------");
const p5 = Promise.resolve()
async function foo () {
  await p5;
  console.log('await')
}
foo()
p5.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))


// 例子6
console.log("-------例子6-------");
const p6 = Promise.resolve()
async function foo () {
  await p6;
  console.log('await')
}
p6.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
foo()


// 例子7
console.log("-------例子7-------");
const p7 = Promise.resolve()
async function foo () {
  await 1;
  console.log('await')
}
foo()
p7.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))


// 例子8
console.log("-------例子8-------");
const p8 = Promise.resolve()
async function foo () {
  await 1;
  console.log('await')
}
p8.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
foo()

复制代码

答案如下:

  • node版本:v10.15.3
// 例子1
console.log("-------例子1-------");
const p1 = Promise.resolve()
async function foo() {
  await p1;
  console.log('await');
}
foo()
p1.then(()=>{
  console.log('promise');
})

/*
-------例子1-------
promise
await
*/

// 例子2
console.log("-------例子2-------");
const p2 = Promise.resolve()
async function foo() {
  await p2;
  console.log('await');
}
p2.then(()=>{
  console.log('promise');
})
foo()
/*
-------例子2-------
promise
await
*/

// 例子3
console.log("-------例子3-------");
const p3 = Promise.resolve()
async function foo() {
  await 1;
  console.log('await');
}
foo()
p3.then(()=>{
  console.log('promise');
})
/*
-------例子3-------
await
promise
*/

// 例子4
console.log("-------例子4-------");
const p4 = Promise.resolve()
async function foo() {
  await 1;
  console.log('await');
}
p4.then(()=>{
  console.log('promise');
})
foo()
/*
-------例子4-------
promise
await
*/

// 例子5
console.log("-------例子5-------");
const p5 = Promise.resolve()
async function foo () {
  await p5;
  console.log('await')
}
foo()
p5.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
/*
-------例子5-------
promise
22
await
33
44
*/

// 例子6
console.log("-------例子6-------");
const p6 = Promise.resolve()
async function foo () {
  await p6;
  console.log('await')
}
p6.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
foo()
/*
-------例子6-------
promise
22
33
await
44
*/


// 例子7
console.log("-------例子7-------");
const p7 = Promise.resolve()
async function foo () {
  await 1;
  console.log('await')
}
foo()
p7.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
/*
-------例子7-------
await
promise
22
33
44
*/

// 例子8
console.log("-------例子8-------");
const p8 = Promise.resolve()
async function foo () {
  await 1;
  console.log('await')
}
p8.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
foo()
/*
-------例子8-------
promise
await
22
33
44
*/

复制代码
  • node版本:v14.15.0
// 例子1
console.log("-------例子1-------");
const p1 = Promise.resolve()
async function foo() {
  await p1;
  console.log('await');
}
foo()
p1.then(()=>{
  console.log('promise');
})

/*
-------例子1-------
await
promise
*/

// 例子2
console.log("-------例子2-------");
const p2 = Promise.resolve()
async function foo() {
  await p2;
  console.log('await');
}
p2.then(()=>{
  console.log('promise');
})
foo()
/*
-------例子2-------
promise
await
*/

// 例子3
console.log("-------例子3-------");
const p3 = Promise.resolve()
async function foo() {
  await 1;
  console.log('await');
}
foo()
p3.then(()=>{
  console.log('promise');
})
/*
-------例子3-------
await
promise
*/

// 例子4
console.log("-------例子4-------");
const p4 = Promise.resolve()
async function foo() {
  await 1;
  console.log('await');
}
p4.then(()=>{
  console.log('promise');
})
foo()
/*
-------例子4-------
promise
await
*/

// 例子5
console.log("-------例子5-------");
const p5 = Promise.resolve()
async function foo () {
  await p5;
  console.log('await')
}
foo()
p5.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
/*
-------例子5-------
await
promise
22
33
44
*/

// 例子6
console.log("-------例子6-------");
const p6 = Promise.resolve()
async function foo () {
  await p6;
  console.log('await')
}
p6.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
foo()
/*
-------例子6-------
promise
await
22
33
44
*/


// 例子7
console.log("-------例子7-------");
const p7 = Promise.resolve()
async function foo () {
  await 1;
  console.log('await')
}
foo()
p7.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
/*
-------例子7-------
await
promise
22
33
44
*/

// 例子8
console.log("-------例子8-------");
const p8 = Promise.resolve()
async function foo () {
  await 1;
  console.log('await')
}
p8.then(()=>{
  console.log('promise');
}).then(()=>console.log(22)).then(()=>console.log(33)).then(()=>console.log(44))
foo()
/*
-------例子8-------
promise
await
22
33
44
*/
复制代码

分析

相信很多人都知道await后面的内容都会被包装在一个promise中。现在有两个疑问需要解决:

  • 这个被包装出来的promise内部是如何实现的
  • await后面跟常量和本身就是promise有什么区别

解开这两个疑问基本上就知道async/await和promise的执行顺序了。 这里有一篇博文可以解开我们的疑问。

我把这里面的主要结论直接摘取出来:

在Node.js 12之前,await经过V8引擎的编译后,会至少生成三个微任务。

这里面存在冗余的运算,所以后来(Node.js 12)进行了优化。

如果入参是 promise,则原封不动地返回,只封装必要的 promise。这个操作在值已经是 promose 的情况下可以省去一个额外的 promise 和两个微任务。

总结

  • 在v10-v12版本,如果await之后是promise,则在外面还会被继续包装promise,在内部会产生三个微任务;如果await之后不是promise,则把该值包装成promise,此时只有一层。
  • 在v12版本之后,await之后已经全部都优化成只有一个微任务。
文章分类
前端
文章标签