前端应用启动加载顺序本质是事件循环与浏览器机制协同作用:html解析遇同步脚本即阻塞并执行,同步代码入调用栈;解析完毕后首轮事件循环先清空微任务(如promise.then),再执行宏任务(如settimeout);css阻塞渲染和后续脚本,async/defer影响脚本执行时机;domcontentloaded和load均为宏任务,但触发时机由资源加载状态决定。

前端应用启动时的加载顺序,本质是 JavaScript 事件循环(Event Loop)与浏览器加载机制协同作用的结果。理解它,关键不是死记“先执行什么、后执行什么”,而是看清宏任务、微任务、资源加载和渲染阶段如何穿插推进。
HTML 解析与同步脚本阻塞
浏览器从上到下解析 HTML,遇到 <script></script> 标签(无 async 或 defer)会立即下载、编译并执行 JS —— 这个过程是同步且阻塞 DOM 构建的。此时 JS 中的同步代码(如 console.log、函数定义、变量赋值)直接进入调用栈运行;而 setTimeout、Promise 等异步操作则被挂起,等待后续事件循环调度。
宏任务与微任务的首次调度时机
HTML 解析完成、同步脚本执行完毕后,浏览器进入第一个事件循环周期:
-
宏任务队列:已排队的有 HTML 解析结束、初始
setTimeout(fn, 0)、setInterval、I/O 回调等 -
微任务队列:所有已 resolve/reject 的
Promise.then/catch/finally、MutationObserver回调会在此轮宏任务结束后立即清空
例如:页面中有一个 new Promise(resolve => { console.log('1'); resolve(); }).then(() => console.log('2')),输出一定是 1 → 2,因为 then 是微任务,在当前宏任务(脚本执行)结束后立刻执行,不等到下一轮。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
资源加载(CSS/JS/图片)如何影响事件循环
CSS 不是 JS 执行的一部分,但 <link rel="stylesheet"> 会阻塞渲染(render blocking),且如果 CSS 后面紧跟 <script></script>,还会阻塞该脚本执行(浏览器为避免 FOUC,会等待样式表就绪)。而带 async 的脚本下载不阻塞 HTML 解析,下载完立即执行(作为独立宏任务);defer 脚本则延迟到 DOM 解析完成后、DOMContentLoaded 之前按顺序执行。
DOMContentLoaded 与 load 的触发位置
这两个事件本身是宏任务,但它们的触发时机由浏览器内部状态决定:
-
DOMContentLoaded:DOM 树构建完成、所有同步脚本执行完、defer脚本也执行完时触发 —— 它在第一个事件循环周期之后、可能含多个微任务清空后发生 -
load:所有资源(图片、CSS、异步脚本等)都加载完毕才触发 —— 它通常比DOMContentLoaded晚,且自身回调会被推入宏任务队列
这意味着:在 DOMContentLoaded 回调里注册的 Promise.then,仍属于微任务,会在该回调执行完后立即运行,早于 load 事件回调。
不复杂但容易忽略的是:事件循环不“等待”资源加载,它只调度已就绪的任务;而资源加载是否阻塞 JS 执行或渲染,取决于标签属性和浏览器策略。抓住宏任务入口点(HTML 解析、脚本执行、定时器到期)、微任务清空时机(每轮宏任务后)、以及外部资源对执行流的干预(阻塞 or 异步),就能稳稳理清启动全过程。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










