学习js异步编程,看这篇文章就够了

122 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情


前言

为什么js执行的异步顺序总是和想象不一致,期望的输出和实际的输出不一样,会导致页面显示有问题,我认为还是因为我们自身的基础不够牢实,比如对宏任务与微任务不够了解,eventloop相关的知识掌握的比较薄弱,本文会对这些知识进行一个整理,巩固我们的基础。

异步编程的方式

在不同的场景和不同的需求下,我们需要选择最合适的异步编程方式,来介绍下他们与他们的优缺点。

1. 回调函数

  • 优点:解决了同步的问题
  • 缺点: 缺乏顺序性,嵌套函数存在耦合性,嵌套函数过多的话,存在错误很难处理

2. promise

  • 优点:解决了回调低于的问题
  • 缺点:无法取消promise,错误需要通过回调函数来捕获

3. async await

  • 优点:代码清晰,解决了回调地狱的问题,不用像promise写这么多then
  • 缺点:await将异步改为同步,如果多个异步操作没有依赖性而使用await会导致性能降低

4. generator

  • 优点:代码直观,阅读起来接近同步编程,代码量很少(建议配合co库使用)
  • 缺点:如果不写一个执行器的话,可读性极差,嵌套很多

4. setTimeout/setInterval

  • 优点:定时器,某些场景下唯一选择
  • 缺点:回调函数的执行时间不一定准确

5. 事件监听 addEventlistener

  • 优点:监听事件的唯一选择,某些场景下不可替代

6. 发布订阅/观察者模式(与addEventListener相同)

eventloop

概念

JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。

浏览器执行顺序

  1. 一开始整段脚本作为第一个宏任务执行
  2. 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  3. 当前宏任务执行完出队,检查微任务队列,如果有则依次执行,直到微任务队列为空
  4. 执行浏览器 UI 线程的渲染工作
  5. 检查是否有Web worker任务,有则执行
  6. 执行队首新的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空

宏任务与微任务有哪些

宏任务

  • 主线程(任务队列)
    1. js脚本
    2. 用户交互事件
    3. 网络请求、文件读写完成事件等等
    4. 渲染事件requestAnimationFrame
  • 延迟队列
    1. setTimeout
    2. setInterval
    3. setImmediate

微任务

每一个宏任务中定义一个微任务队列,当该宏任务执行完成,会检查其中的微任务队列,如果为空则直接执行下一个宏任务,如果不为空,则依次执行微任务,执行完成才去执行下一个宏任务。

  1. MutationObserver(浏览器)
  2. Promise.then(或.reject)
  3. process.nextTick(nodejs)
  4. Object.observe(不推荐使用)
  5. 以 Promise 为基础开发的其他技术(比如fetch API), 还包括 V8 的垃圾回收过程