JS面试题笔记5-异步进阶

event loop

  • event loop (事件循环/事件轮询)

    • JS 是单线程运行的
    • 异步要基于回调实现
    • event loop 就是异步回调的实现原理
    • JS 如何执行?
      从前到后,一行一行执行
      若某一行执行报错,则停止下面代码的执行
      先把同步代码执行完,再执行异步代码
    • event loop 过程
      ① 同步代码,一行一行放在 Call Stack (调用栈) 执行
      ② 遇到异步,会先记录下,等待时机 (定时、网络请求等)
      ③ 时机到了,就移动到 Callback Queue
      ④ 若 Call Stack 为空 (即同步代码执行完) Event Loop 开始工作
      ⑤ 轮询查找 Callback Queue ,若有则移动到 Call Stack 执行
      ⑥ 继续轮询查找
    • DOM 事件和 event loop
      JS 是单线程的
      异步使用回调,基于 event loop
      DOM 事件也使用回调,基于 event loop

Promise

  • Promise 的三种状态

    • pending / resolved / rejected
    • pending -> resolved 或 pending -> rejected
    • 变化不可逆
    const p1 = new Promise((resolve, reject) => {})
    console.log('p1', p1)
    
    const p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve()
      })
    })
    console.log('p2', p2) // pending 开始打印时
    setTimeout(() => console.log('p2-setTimeout', p2)) // resolved
    
    const p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject()
      })
    })
    console.log('p3', p3)
    setTimeout(() => console.log('p3-setTimeout', p3)) // resolved
    
    • pending 状态,不会触发 then 和 catch
    • resolved 状态,会触发后续的 then 回调函数
    • rejected 状态,会触发后续的 catch 回调函数
    const p1 = Promise.resolve(100)
    // console.log('p1', p1)
    p1.then((data) => {
      console.log('data', data) // data 100
    }).catch((err) => {
      console.error('err', err)
    })
    
    const p2 = Promise.reject('err')
    console.log('p2', p2)
    p2.then((data) => {
      console.log('data2', data)
    }).catch((err) => {
      console.error('err2', err) // err2 err
    })
    
  • then 和 catch 改变状态

    then 正常返回 resolved ,有报错则返回 rejected
    catch 正常返回 resolved ,有错则返回 rejected

    const p1 = Promise.resolve().then(() => {
      return 100
    })
    // location('p1', p1) // resolved 触发后续 then 回调
    p1.then(() => {
      console.log('123')
    })
    
    const p2 = Promise.resolve().then(() => {
      return new Error('then error')
    })
    p2.then(() => {
      console.log('456')
    }).catch((err) => {
      console.error('err100', err)
    })
    // location('p2', p2) // rejected 触发后续 catch 回调
    
    const p3 = Promise.reject('my error').catch((err) => {
      console.error(err)
    })
    console.log('p3', p3) // resolved 触发 then 回调
    p3.then(() => {
      console.log(100)
    })
    
    const p4 = Promise.reject('my error').catch((err) => {
      throw new Error('catch err')
    })
    console.log('p4', p4) // rejected 触发 catch 回调
    p4.then(() => {
      console.log(200)
    }).catch(() => {
      console.log('some err')
    }) // resolved
    
  • Promise then 和 catch 的连接

    // 第一题
    Promise.resolve()
      .then(() => {
        console.log(1) // 1
      })
      .catch(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3) // 3
      }) // resolved
    
    // 第二题
    Promise.resolve()
      .then(() => {
        console.log(1) // 1
        throw new Error('error1')
      })
      .catch(() => {
        // resolved
        console.log(2) // 2
      })
      .then(() => {
        console.log(3) // 3
      }) // resolved
    
    // 第三题
    Promise.resolve()
      .then(() => {
        // rejected
        console.log(1) // 1
        throw new Error('error1')
      })
      .catch(() => {
        // resolved
        console.log(2) // 2
      })
      .catch(() => {
        console.log(3)
      })
    

async/await

  • async/await 语法

    async/await 是同步语法,彻底消除回调函数

  • async/await 和 Promise 的关系

    • async/await 和 Promise 并不互斥,二者相辅相成
    • 执行 async 函数,返回的是 Promise 对象
    • await 相当于 Promise 的 then (若 await 后为值,则封装为 Promise)
    • try…catch 可捕获异常,代替了 Promise 的 catch
    !async function () {
      const p4 = Promise.reject('err') // rejected 状态
      try {
        const res = await p4 // await 相当于 Promise then
      } catch (ex) {
        console.error(ex) // try...catch 相当于 Promise 的 catch
      }
    }
    
  • 异步的本质

    • async/await 是消灭异步回调的终极武器
    • JS 还是单线程,还的是有异步,还的是基于 event loop
    • async/await 只是一个语法糖
  • 外加 async/await 的顺序问题

    async function async1() {
      console.log('async1 start') // 2 重要
      await async2() // undefined
      // ★ await 后面都可以看作是 callback 里的内容,即异步回调
      // 类似 event loop 中 setTimeout
      console.log('async1 end') // 5
      await async3()
      console.log('async1 end 2') // 7
    }
    
    async function async2() {
      console.log('async2') // 3 重要
    }
    
    async function async3() {
      console.log('async3') // 6
    }
    
    console.log('script start') // 1
    async1()
    console.log('script end') // 4 (同步代码执行完 event loop)
    

for…of

  • for…of

    • for…in(以及 forEach for) 是常规的同步遍历
    • for…of 常用于异步的遍历
    function muti(num) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(num * num)
        }, 1000)
      })
    }
    
    const nums = [1, 2, 3]
    
    !(async function () {
      for (let i of nums) {
        const res = await muti(i)
        console.log(res)
      }
    })()
    

宏任务和微任务

  • 宏任务 macroTask 和微任务 microTask

    • 宏任务:setTimeout setInterval Ajax DOM 事件 – DOM 渲染后触发
    • 微任务:Promise async/await – DOM 渲染前触发
    • 微任务执行时机比宏任务要早
  • event loop 和 DOM 渲染

    • JS 是单线程的,而且和 DOM 渲染共用一个线程
    • JS 执行的时候,得留一些时机供 DOM 渲染
      ① Call Stack 空闲 (每次轮询结束、同步任务执行完)
      ② 尝试 DOM 渲染 (DOM 结构若有改变则重新渲染)
      ③ 触发下一次 Event Loop
  • 微任务和宏任务的根本区别

    • 微任务是 ES6 语法规定的
    • 宏任务是由浏览器规定的
    • 执行 Promise 时不会置于 Web APIs 中,而置于 micro task queue
    • 微任务执行流程:
      ① Call Stack 清空
      ② 执行当前的微任务
      ③ 尝试 DOM 渲染
      ④ 触发 Event Loop
  • 执行顺序问题

    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 ,执行宏任务
    

你可能感兴趣的:(前端面试题笔记,javascript)