JS事件循环详解

为什么JavaScript是单线程

JavaScript语言的一大特点就是单线程,即同一时间内,JavaScript只能做一件事。

其实,JavaScript的单线程特点和它的用途是有关系的,试想,如果JavaScript是多线程的,此时有两个线程,一个线程修改某个DOM元素的内容,另一个线程又去删除这个DOM,那么这时浏览器就不知道该怎么办了,造成不必要的混淆。

所以,为了避免复杂,JavaScript做成单线程语言,这样就很清晰了。

任务队列

因为JavaScript的单线程机制,这就意味着所有的任务都必须排队执行,前一个任务执行完成,后一个任务才会执行,如果此时队列中有一个非常耗时的操作,后一个任务就不得不持续等待。

但是JavaScript设计者不会允许这样的事情发生,所以JavaScript会将异步事件挂在与当前执行栈不同的队列中,这个队列我们称之为任务队列

JavaScript主线程执行时,会从执行栈顶取出一个任务执行,执行完后再取任务执行,周而复始。

任务分类

在JavaScript中,我们将全部的任务分为两种:同步任务和异步任务。

异步任务又分为宏任务微任务

  • 宏任务(macrotask):一些异步任务的回调函数会依次进入宏任务队列(macro task queue),这些异步任务包括:
    • setTimeout
    • setInterval
    • setImmediate (Node独有)
    • requestAnimationFrame (浏览器独有)
    • I/O
    • UI rendering (浏览器独有)
  • 微任务(microtask):另一些异步任务的回调函数会依次进入宏任务队列(micro task queue),包括:
    • process.nextTick (Node独有)
    • Promise.then
    • Object.observe
    • MutationObserver

Event Loop

先来看一张图:

这张图将浏览器的event loop完整的流程描述了出来,具体如下:

  1. 执行全局Script同步代码,这些同步代码有一些是同步语句,有一些是异步语句(比如setTimeout等);
  2. 全局Script代码执行完毕后,调用栈Stack会清空;
  3. 从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;
  4. 继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行;
  5. microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈Stack也为空;
  6. 取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;
  7. 执行完毕后,调用栈Stack为空;
  8. 重复第3-7的步骤;

面试题详解

题目:

javascript 复制代码
setTimeout(function () {
    console.log(1);
});
new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})
console.log(4);

分析下代码的执行过程:

  1. 执行script
  2. 第一行遇到setTimeout,宏任务,放到宏任务队列中,继续执行
  3. 第4行遇到promisepromise里面的任务是同步任务,是立即执行的,所以打印2,并执行resolve
  4. 第七行,遇到then,微任务,放到微任务队列中,继续执行
  5. 第十行,遇到打印语句,同步任务,直接执行打印4,
  6. 此时,执行栈空了,JavaScript开始从微任务队列中依次取出微任务压入执行栈中执行,打印3
  7. 微任务队列空了,JavaScript开始从宏任务队列中依次取出宏任务压入执行栈中执行,打印1. 所以最终结果是2,4,3,1
相关推荐
rockey6278 小时前
C#脚本引擎之AScript与Jurassic、Jint对比
javascript·c#·.net·js·script·动态脚本
lzhdim9 小时前
12、JavaScript常见的内存泄露问题 - JavaScript学习系列文章
开发语言·前端·javascript·学习·ecmascript
kyriewen13 小时前
我踩了3次同一个坑才明白:JavaScript里比0.1+0.2更隐蔽的5个数字陷阱
前端·javascript·面试
zhifou12345614 小时前
Vue基础(二)
前端·javascript·vue.js
默_笙14 小时前
🙃 我让爬虫终于看到了我的网站,后端同事说"这也行?"(下):App Router 全栈实战
前端·javascript
九九落14 小时前
JavaScript 实现北京时间精确到毫秒显示:UTC+8、Asia/Shanghai 与在线时间校准详解
开发语言·javascript·ecmascript
FEF前端团队15 小时前
小程序微信支付 V3 接入实战手册:从商户配置到前后端落地
javascript·后端·node.js
悟空瞎说16 小时前
# Dispatch(GCD)苹果官方文档 白话文讲解(Objective‑C 版本文档)
javascript
七牛开发者17 小时前
Coding Agent 如何跑稳长任务?从上下文管理到运行时状态
前端·javascript·后端
七牛开发者17 小时前
拆解 DeepSeek Harness:Profile 与 Bundle 如何装配运行时
前端·javascript·后端