JavaScript单线程执行意味着同一时间只能处理一个任务,导致耗时操作会阻塞页面响应;为优化体验,浏览器通过async和defer属性实现脚本异步加载,避免阻塞HTML解析,其中async脚本下载后立即执行,不保证顺序,而defer脚本在DOM解析完成后按序执行;更复杂的执行顺序由事件循环机制调控,它协调宏任务(如setTimeout)与微任务(如Promise回调),确保微任务优先于宏任务执行,从而形成一套高效、非阻塞的异步编程模型。

浏览器中的JavaScript执行,从宏观上看是单线程、同步阻塞的,但现代前端开发中,异步机制(如事件循环、Promise、async/await)和脚本加载优化(如
async
defer
要说浏览器里JavaScript的执行顺序,这事儿真不是一两句话能讲清的,它像个层层嵌套的洋葱。最基础的,浏览器在解析HTML文档时,如果遇到
<script>
但我们都知道,这种阻塞体验太差了,尤其是脚本文件大的时候,页面会白屏很久。所以,后来就有了各种优化手段。比如,把
<script>
<body>
async
defer
async
defer
DOMContentLoaded
除了这些加载层面的优化,JavaScript语言本身也进化出了强大的异步能力。事件循环(Event Loop)是理解JS异步执行的关键。它把那些耗时的操作,比如网络请求、定时器、用户交互,从主线程上“剥离”出去,交给Web APIs处理,等结果准备好了,再把回调函数放到任务队列里,等待主线程空闲时去执行。这里面还有微任务(Microtask)和宏任务(Macrotask)的区别,微任务(比如Promise的回调)总是优先于宏任务(比如
setTimeout
所以,一个页面上,你可能会看到
<script>
async
defer
JavaScript是单线程的,这个概念初听起来可能有点反直觉,毕竟我们平时用浏览器感觉它能同时做很多事。但这里说的“单线程”特指JS引擎在执行代码时,只有一个主线程负责处理所有的任务。这就意味着,同一时间,它只能做一件事。
这就像一个厨师,他一次只能炒一道菜。如果他正在切菜(执行同步代码),那么他就不能同时洗碗、烧水。一旦有任何一个任务需要长时间运行,比如一个复杂的计算循环,或者一个没有
async
defer
我记得刚开始写前端的时候,就吃过这种亏,一个不小心写了个死循环,整个浏览器标签页就挂了。这深刻说明了单线程的局限性。为了避免这种尴尬,开发者必须学会把耗时的操作“外包”出去,或者拆分成小块,让主线程能定期喘口气,去处理UI渲染、用户输入等其他重要任务。这也是为什么异步编程在JavaScript中如此重要的原因,它不是为了多线程,而是为了在单线程模型下模拟并发,提升用户体验。
async
defer
async
defer
<script>
想象一下,没有
async
defer
<script src="..."
有了
async
async
async
而
defer
DOMContentLoaded
defer
我个人在项目里,如果脚本之间没有明确依赖,或者优先级不高,会倾向于用
async
defer
事件循环(Event Loop),这玩意儿是JavaScript异步编程的“幕后英雄”,也是很多初学者(包括我当年)觉得最烧脑但也最关键的概念。如果你不理解它,那么
setTimeout(..., 0)
setTimeout
简单来说,事件循环就是浏览器(或Node.js)用来协调主线程和各种异步任务的一套机制。它由几个核心部分组成:
setTimeout
fetch
.then()
.catch()
.finally()
MutationObserver
事件循环的工作流程大致是这样的:主线程会先清空调用栈中的所有同步代码。一旦调用栈为空,事件循环就开始检查微任务队列。如果有微任务,它会把所有微任务都取出来,逐个推入调用栈执行,直到微任务队列清空。清空微任务队列后,事件循环才会去宏任务队列中取出一个宏任务(比如一个
setTimeout
这种机制就解释了为什么
Promise.resolve().then(...)
setTimeout(..., 0)
Promise
setTimeout
以上就是浏览器JS执行顺序规则?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号