风静夜

风静夜

JavaScript 异步

JavaScript 为什么需要异步

  • JavaScript是一门单线程的语言,运行在浏览器中,
  • 如果JavaScript所有操作都为同步,当执行一些比较耗时的操作时,浏览器页面就会卡顿,
  • 为了优化用户体验,针对这种场景,JavaScript被设计为可以进行异步操作。

说一下异步是如何实现的

  • 一切的异步都是对回调函数的封装,
  • 在ES6以前的时代,实现异步是通过回调函数实现的,
  • 在ES6中,引入了社区产物Promise,通过.then()和.catch()实现异步;同时也可以使用Generator函数进行异步操作,
  • 在ES7中,引入了Promise的语法糖 async/await。

为什么会引入 Promise

  • Promise本质是实现了回调函数的封装,这是为了解决“回调地狱”的问题,
  • 当多层回调函数嵌套时,代码可读性会严重降低,同时多层callback的结构也会难以维护,这种情况就是“回调地狱”,
  • 如果使用了Promise,即使需要多层的异步操作,也可以用链式写法,这样的代码更利于人理解,更利于后期的维护。

如何使用 Generator 处理异步操作

  • 定义Generator函数时,需要使用function* xxx(){}
  • 当调用Generator函数时,该函数不会立即执行,而是会返回一个可迭代的对象,
  • 当调用了返回可迭代对象中的 next() 方法时,会执行之前的函数中的代码,直到遇到 yield 关键字时,再次暂停,
  • 调用next()方法会返回一个对象,包含value属性和done属性,value 是遇到 yield 关键字之前返回的值,而done 属性是当前迭代器是否执行完毕。

浏览器中的微任务

  • process.nextTick
  • Promise
  • Async/Await(实际就是promise)
  • MutationObserver(html5新特性)

浏览器中的宏任务

  • setTimeout
  • setInterval
  • setImmediate
  • I/O

说一下事件轮询机制

  1. 首先会一行一行地将同步代码放入调用栈中执行,
  2. 如果遇到了异步代码,会等待时机,
  3. 如果时机已到,异步代码为宏任务时会放入宏任务队列中等待执行,微任务会放入微任务队列中等待执行,
  4. 当同步代码执行完毕时,会先执行微任务队列中的微任务,调用顺序为先进入队列先调用,
  5. 微任务队列清空时,如果DOM结构改变,会尝试DOM结构的渲染,
  6. 执行宏任务队列的宏任务,
  7. 当宏任务产生了新的微任务时,在执行完当前宏任务时会优先清空微任务队列,
  8. 循环4-7这个过程。