前端八股整理(代码输出 02)|宏任务微任务 promise await 代码输出题

本文速览

一句话总结:
本文主要讲清楚 JavaScript 事件循环、宏任务、微任务、Promise 和 async/await 的执行顺序,帮助你理解代码输出题的核心判断逻辑,并能解决前端面试中常见的异步执行顺序题目。

适合人群:
适合正在准备前端面试、学习 JavaScript 异步机制、容易在 Promise / async / await 输出题中出错的同学阅读。

核心问题:

  1. 宏任务和微任务的执行顺序是什么?

  2. Promise 的 executor 和 then 分别什么时候执行?

  3. setTimeout、Promise、async/await 混在一起时应该怎么判断输出顺序?

  4. 面试中的异步代码输出题有哪些常见考点?

你将学会:

  • 理解事件循环中宏任务和微任务的基本执行流程

  • 掌握 Promise executor 同步执行、then 回调进入微任务队列的规则

  • 理解 Promise 状态一旦改变就不可逆

  • 理解 async / await 和 Promise.then 之间的关系

  • 能够独立分析常见的 JavaScript 异步代码输出题

相关关键词:
JavaScript、事件循环、宏任务、微任务、Promise、async/await、setTimeout、代码输出题、前端面试

1.事件循环基础

1.1 宏任务与微任务执行顺序

执行顺序如下:

  • 执行一次宏任务(最开始会是整个srcipt所以上面的例子会先执行console.log(1))
  • 执行过程中如果遇到宏任务,就放进宏任务列队
  • 执行过程中如果遇到微任务,就放进微任务列队
  • 当执行栈空了,先检查微任务列队,如果有微任务,就依序执行直到微任务列队为空
  • 接着进行浏览器的渲染,渲然完后开始下一个宏任务(回到最开始的步骤)

1.2 Promise 执行机制

  • promise中的执行器函数(executor)是同步执行的,也就是直接加入到执行栈中执行
  • resolve 触发后,才将对应的.then 的部分加入微任务队列
  • 如果既存在 resolve,又存在 reject,那么只会执行他们之中的第一个,因为Promise 状态一旦从 pending 变成 fulfilled,就不可能变成 fulfailed

1.3 setTimeout 执行机制

  • setTimeout 加入宏任务队列中

1.4 async / await 执行机制

  • await async2() 会同步立即执行 async2,并且把 await 后面的代码包装成一个「微任务」去执行

async/await 可以理解成 Promise.then() 的语法糖

await 后面跟一个普通函数的时候,会先正常执行,拿到返回值后,再用 Promise.resolve 包一层,然后后面的代码进行到微任务队列中继续执行

2.实战题目

2.1 模拟实战

给出下面代码的输出:

console.log('A');

const p = new Promise((resolve, reject) => {
  // 这段就是 executor(执行器函数)
  console.log('B: executor start');

  resolve('OK');                        

  console.log('C: executor end');
});

p.then((value) => {
  console.log('E: then', value);
});

console.log('D');
A
B: executor start
C: executor end
D
E: then
console.log(1);

setTimeout(function () {
  console.log(2);
}, 0);

Promise.resolve()
  .then(function () {
    console.log(3);
  })
  .then(function () {
    console.log(4);
  });
1
3
4
2
console.log("begins");

setTimeout(() => {                                    
  console.log("setTimeout 1");                         
  Promise.resolve().then(() => {                          
    console.log("promise 1");                          
  });
}, 0);

new Promise(function (resolve, reject) {         
  console.log("promise 2");        
  setTimeout(function () {                     
    console.log("setTimeout 2");
    resolve("resolve 1");
  }, 0);
}).then((res) => {                       
  console.log("dot then 1");
  setTimeout(() => {                  
    console.log(res);
  }, 0);
});
begins
promise 2
setTimeout 1
promise 1
setTimeout 2
dot then 1
resolve 1
async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function () {
  console.log("setTimeout");
}, 0);

async1();

new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("promise2");
});

console.log("script end");
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

2.2 真题实战

来源:腾讯音乐 暑期二面

new Promise((resolve, reject) => {
  console.log("p1");
  resolve();
  console.log("p2")
  reject();
}).then(() => {
  console.log("then1");
  new Promise((resolve, reject) => {
    console.log("p2");
    resolve();
  }).then(() => {
    console.log("then2");
  }).then(() => {
    console.log("then3");
  });
}, () => {
  console.log("then4")
})
p1
p2
then1
p2
then2
then3
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐