【字节面试题】有关async/await的理解

3,266 阅读3分钟

前言:

刷面试题的意义在于,在一道题中把之前学到了知识点覆盖串联起来,加深印象,提高能力,而不是背答案。下面是一道字节跳动的面试题,可以复习到异步,promise,async/await,事件执行机制,宏任务微任务... 话不多说,直接上题:

题目:

function getJson() {
   return new Promise((resolve, reject) => {
     setTimeout(function() {
       console.log(2);
       resolve(2)
     }, 2000)
   })
 }
 
async function testAsync() {
    await getJson()  //事件循环机制  await 下面的代码会去到下一次事件循环机制
    console.log(3);
  }
testAsync()

1. 输出结果是什么?

输出结果: 2 3

在事件循环机制中,await 下面的代码会去到下一次事件循环机制

2. async/await 原理是什么?

首先我们知道,async/await是ES7中的异步语法,我们可以从异步编程的发展史开始和面试官聊。 因为 JavaScript是单线程执行机制,所以为了提高效率我们使用异步编程。

异步编程的发展历史:

1. 回调函数:

缺点是不利于代码的阅读维护,各部分之间高度耦合,流程会很乱。每个人物只能指定一个回调函数。不能捕获异常 (try catch 同步执行,回调函数会加入队列,无法捕获错误)

2. Promise

Promise 不仅可以避免回调地狱,还可以统一捕获失败的原因,目前也应用广泛

3. Generator

生成器是一个函数,需要加* ,可以用来生成迭代器。生成器函数和普通函数不一样,普通函数是一旦调用一定会执行完,但是生成器函数中间可以暂停。生成器和普通函数不一样,调用它并不会立即执行。它会返回此生成器的迭代器,迭代器是一个对象,每调用一次next就可以返回一个值对象

4. co

随着前端的迅速发展,大神们觉得要像同步代码一样写异步,co问世了,co是 TJ 大神结合了promise 和 生成器 的一个库,实际上还是帮助我们自动执行迭代器

5. async/await

async await是语法糖,内部是generator+promise实现 async函数就是将Generator函数的星号(*)替换成async,将yield替换成await

事件执行机制: EventLoop

  1. 首先会执行同步操作
  2. 执行完后查看执行栈是否为空
  3. 如果为空,查看是否有微任务需要执行,如果有放入执行栈
  4. 在查看是否有宏任务需要执行,如果有放入执行栈

宏任务有哪些:

  1. script(整体代码)
  2. setTimeout
  3. setInterval
  4. I/O
  5. UI交互事件
  6. postMessage
  7. MessageChannel
  8. setImmediate(Node.js 环境)

微任务有哪些:

  1. Promise.then
  2. Object.observe
  3. MutaionObserver
  4. process.nextTick(Node.js 环境)

3. 将这段代码翻译成promise

当我们理解了async/await 就是语法糖,它的本质还是Promise,async相当于相当于Promise.then(),await相当于.then()括号里面的操作。所以翻译成Promise后就是下面这段代码:

// async function testAsync() {
  //   await getJson()  
  //   console.log(3);
  // }
  //相当于:
function testAsync(){
    return Promise.resolve().then(()=>{
      return getJson()
    }).then(()=>{
      console.log(3);
    })
  }

参考文献:

1. 异步编程的前世今生