前端JS面试题-进阶-异步


本文主要包括如下内容:

  • event loop
  • async / await
  • 微任务 宏任务

题目:

  1. 请描述event loop(事件循环/事件轮询)的机制,可画图
  2. 什么是宏任务和微任务,两者有什么区别
  3. promise有哪三种状态,如何变化
  4. 场景题,promise then 和 catch 的链接
  5. 场景题 async/await语法
  6. 场景题 promise 和 setTImeout的打印顺序
  7. 场景题 外加async/await的顺序问题

1. 请描述event loop(事件循环/事件轮询)的机制

  • 同步代码,一行一行放在 call stack 【主执行栈】 执行
  • 遇到异步,会先“记录”下,等待时机(定时、网络请求等)
  • 时机到了,就将该异步事件移动到 callback queue 【回调队列】
  • 一旦同步代码执行完, call stack 空了,就会立刻执行 event loop 机制(引擎启动)
  • event loop 不断循环 callback queue,如有则移动到call stack 执行
  • 然后继续轮询查找

2. 什么是宏任务和微任务,两者有什么区别

  • 宏任务:setTimeout setInterval Ajax DOM事件;DOM 渲染后触发
  • 微任务:promise async/await;DOM渲染前触发
  • 因此,微任务执行时机比宏任务要早

3. promise有哪三种状态,如何变化

4. 场景题,promise then 和 catch 的链接

  • 第一题: 1 3
  • 第二题: 1 2 3
  • 第三题: 1 2

5. 场景题 async/await语法

  • 执行async 函数,返回的是promise对象
  • await相当于promise的then,处理不了catch
  • 左侧题目:
    a 是 promise
    b 是 100
  • 右侧题目 打印顺序
    a 100
    b 200
    报错

6. 场景题 promise 和 setTImeout的打印顺序


100 400 300 200

  • 先执行同步代码,再执行异步代码
  • 先执行微任务,再执行宏任务

7. 场景题 外加async/await的顺序问题

async function async1() {
  console.log('async1 start')  // 2
  await async2()
  // await 后面地都作为回调内容 —— 微任务
  console.log('async1 end')  // 6
}
async function async2() {
  console.log('async2')  // 3
}
console.log('script start')  // 1
setTimeout(function () {  //宏任务
  console.log('setTimeout')  // 8
}, 0)
async1()
// 初始化 promise 时,传入地函数会立刻被执行
new Promise (function (resolve) {
  console.log('promise1')  // 4
  resolve()
}).then (function () {  // 微任务
  console.log('promise2')  // 7
})
console.log('script end')  // 5

同步代码执行完毕(event loop — call stack 被清空)
执行微任务
尝试触发 DOM 渲染
触发 event loop,执行宏任务

知识点

1.JS如何执行

  • 从前到后,一行一行执行
  • 如何某一行执行报错,则停止下面代码的执行
  • 先把同步代码执行完,再执行异步
console.log('hi')
setTimeout(function cb1() {
	console.log('cb1)
}, 5000)
console.log('bye)
// 打印顺序: hi bye cb1

2. event loop 事件循环/事件轮询

  • JS是单线程运行的
  • 异步要基于回调来实现
  • event loop 就是异步回调的实现原理

3. event loop 的执行过程

  • 同步代码,一行一行放在 call stack 【主执行栈】 执行
  • 遇到异步,会先“记录”下,等待时机(定时、网络请求等)
  • 时机到了,就将该异步事件移动到 callback queue 【回调队列】
  • 一旦同步代码执行完, call stack 空了,就会立刻执行 event loop 机制(引擎启动)
  • event loop 不断循环 callback queue,如有则移动到call stack 执行
  • 然后继续轮询查找

4. DOM事件和 event loop

  • 触发回调的时机不一样
  • JS是单线程运行的,异步(ajax setTimeout等)使用回调,基于event loop;DOM事件也使用回调,基于event loop
  • 所以它们两个在执行上有冲突,那就存在执行的先后次序

5. async / awiat

  • 异步回调 会产生 callback hell
  • promise then catch 是链式调用,但也是基于回调函数
  • async/await是同步语法,彻底消灭回调函数(使用同步的语法方式来写异步函数)

6. async/await 和 promise 的关系

  • async/await 是消灭异步回调的终极武器
  • 但和promise并不互斥
  • 反而,两者相辅相成
  • 执行async 函数,返回的是promise对象
  • await相当于promise的then,处理不了catch
  • try...catch可捕获异常,代替了promise的catch

7. 异步的本质

  • async/await 是消灭异步回调的终极武器——价值和意义
  • JS还是单线程,还是得要异步,还是得基于event loop
  • async/await 只是一个语法糖

8. for ... of

  • for ... in / forEach for 是常规的同步遍历,如果用于异步循环会报错
  • for ... of 常用于异步的循环

9. 宏任务 macroTask 和微任务 microTask

  • 宏任务:setTimeout setInterval Ajax DOM事件;DOM 渲染后触发
  • 微任务:promise async/await;DOM渲染前触发
  • 因此,微任务执行时机比宏任务要早

10. event loop 和 DOM渲染

  • JS是单线程,而且和DOM渲染共用一个线程
  1. 每次 call stack 清空(既每次轮询结束),即同步任务执行完
  2. 都是 DOM 重新渲染的机会,DOM 结构如有改变则重新渲染
  3. 然后再去触发下一次 event loop