异步
css 的加载
- CSS 文件是并行下载的
- CSS 的下载不会阻塞后面 JS 的下载,CSS 加载解析完成之后,再执行下面的 JS。
JS 的加载
- 并行加载 JS 文件
- 加载或者执行 JS 时会阻塞构建 DOM 树,只有等到 js 执行完毕,浏览器才会继续解析 DOM。没有 DOM 树,浏览器就无法渲染。
- 这是由于 JS 引擎线程 与 GUI 渲染线程 互斥。至于互斥的初衷,主要是因为加载的 JS 中可能会创建,删除节点等,这些操作会对 DOM 树 产生影响,如果不阻塞,等浏览器解析完标签生成 DOM 树后,JS 修改了某些节点,那么浏览器又得重新解析,然后重新生成 DOM 树
改变外链 JS 的加载时机、顺序
- defer 和 async 都是异步的,主要的区别在于执行顺序以及执行的时间
- async 标志的脚本文件一旦加载完成就会立即执行,并且不会按照书写顺序,谁下载好了就直接执行
- defer 标志的脚本文件会严格按照书写顺序执行,并且,会在 DOMContentLoaded 事件之前执行
消息队列:是 V8 引擎 除了主线程任务外,额外维护的一个队列,主要存放要执行的任务(函数)
事件循环
- 主线程运行产生了执行栈,在调用执行栈的过程中调用了一些异步函数。
- 当满足异步函数的触发条件时,会将对应的回调函数推送到消息队列中。
- 当主栈中的代码执行完毕时,会触发一次页面渲染,然后创建新的主栈。
- 将消息队列中的回调函数推送到主栈。然后顺序执行主栈的任务。
- 反复循环执行上述过程就是时间循环。
一开始整段脚本作为第一个宏任务执行 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列 当前宏任务执行完出队,检查微任务队列,如果有则依次执行,直到微任务队列为空 执行浏览器 UI 线程的渲染工作 检查是否有 Web worker 任务,有则执行 执行队首新的宏任务,回到 2,依此循环,直到宏任务和微任务队列都为空
node 的 EventLoop
- 执行 定时器回调 的阶段
- 轮询 (英文叫 poll) 阶段
- check 阶段
timer 阶段 I/O 异常回调阶段 空闲、预备状态 (第 2 阶段结束,poll 未触发之前) poll 阶段 check 阶段 关闭事件的回调阶段
浏览器中的微任务是在每个相应的宏任务中执行的,而 nodejs 中的微任务是在不同阶段之间执行的。
process.nextTick 是一个独立于 eventLoop 的任务队列。在每一个 eventLoop 阶段完成后会去检查这个队列,如果里面有任务,会让这部分任务优先于微任务执行