携手创作,共同成长!这是我参与「掘金日新计划 · 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是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。
浏览器执行顺序
- 一开始整段脚本作为第一个宏任务执行
- 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当前宏任务执行完出队,检查微任务队列,如果有则依次执行,直到微任务队列为空
- 执行浏览器 UI 线程的渲染工作
- 检查是否有
Web worker任务,有则执行 - 执行队首新的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空
宏任务与微任务有哪些
宏任务
- 主线程(任务队列)
- js脚本
- 用户交互事件
- 网络请求、文件读写完成事件等等
- 渲染事件
requestAnimationFrame
- 延迟队列
setTimeoutsetIntervalsetImmediate
微任务
每一个宏任务中定义一个微任务队列,当该宏任务执行完成,会检查其中的微任务队列,如果为空则直接执行下一个宏任务,如果不为空,则依次执行微任务,执行完成才去执行下一个宏任务。
- MutationObserver(浏览器)
- Promise.then(或.reject)
- process.nextTick(nodejs)
- Object.observe(不推荐使用)
- 以 Promise 为基础开发的其他技术(比如fetch API), 还包括 V8 的垃圾回收过程