网站
引入
JS 一次只能做一件事。
代码从上往下顺序执行,如果中间有个操作很慢(比如请求网络数据),后面的代码就得一直等着。
为了让慢操作不卡住后面的代码,JS 引入了同步和异步的概念,配合调用栈和任务队列来协调执行。
正文
定义
定义
JS 执行机制是指 JS 代码从进入运行到执行完成的整个流程。核心包括:调用栈(执行同步代码)、任务队列(存放异步回调)、事件循环(协调两者的运转)。
单线程
JS 是单线程语言,同一时刻只能执行一段代码。
这是为了避免多个操作同时修改页面,造成冲突。
比如一个脚本在删除元素,另一个同时在添加元素,页面就乱套了。
单线程意味着代码要排队执行,所以不能让慢操作堵住后面的代码。
同步和异步
同步代码:按顺序一行一行执行,上一行没完成,下一行不开始。
console.log("第一步");
console.log("第二步");
console.log("第三步");输出:
第一步
第二步
第三步异步代码:把慢操作交给浏览器(或 Node.js),自己继续执行后面的代码。等慢操作完成后,再回来处理结果。
console.log("开始");
setTimeout(function () {
console.log("定时器完成");
}, 1000);
console.log("结束");输出:
开始
结束
定时器完成setTimeout 把定时器任务交给浏览器,JS 自己继续往下走。
等 1 秒后定时器到了,浏览器再通知 JS 回来执行回调。
调用栈
调用栈是一个后进先出的结构,用来执行同步代码。
代码执行时,函数被压入栈顶,执行完后弹出。
function a() {
console.log("a");
}
function b() {
a();
console.log("b");
}
b();调用栈的变化过程:
1. b() 进入调用栈
2. a() 进入调用栈(在 b 上面)
3. a() 执行完毕,弹出调用栈
4. b() 继续执行,弹出调用栈
输出:
a
b栈底是先进去的,栈顶是当前正在执行的。
任务队列
异步操作完成后,回调函数不会马上执行,而是先放到任务队列里排队。
任务队列是先进先出的结构,谁先到谁先执行。
setTimeout(function () {
console.log("第一个");
}, 1000);
setTimeout(function () {
console.log("第二个");
}, 500);输出:
第二个
第一个500ms 的先到队列,所以先执行。
事件循环
事件循环(Event Loop)是 JS 执行机制的核心调度员。
它不停地在做一件事:
- 检查调用栈是不是空的
- 如果空了,就去任务队列里拿一个回调放到调用栈里执行
- 执行完再检查调用栈,重复这个过程
用一个图来表示整个流程:
同步代码
│
▼
调用栈(执行)
│
│ 遇到异步操作
▼
交给浏览器处理
│
│ 异步完成
▼
回调进入任务队列
│
│ 调用栈空了
▼
事件循环把回调推到调用栈
│
▼
继续执行
完整的执行流程
看一段综合代码:
console.log("1");
setTimeout(function () {
console.log("2");
}, 0);
console.log("3");执行过程:
console.log("1")进入调用栈,执行,输出1,弹出setTimeout进入调用栈,把定时器交给浏览器,弹出console.log("3")进入调用栈,执行,输出3,弹出- 调用栈空了,定时器已到时间,回调进入任务队列
- 事件循环把回调推到调用栈,执行,输出
2,弹出
输出:
1
3
2即使 setTimeout 设置 0 毫秒,回调也要等同步代码全部执行完才轮到它。
同步异步对比
| 对比项 | 同步 | 异步 |
|---|---|---|
| 执行方式 | 按顺序逐行执行 | 交给浏览器,完成后回调 |
| 是否阻塞 | 会阻塞后面的代码 | 不阻塞 |
| 常见场景 | 普通计算、DOM 操作 | 定时器、网络请求、文件读取 |
| 执行位置 | 调用栈 | 先浏览器处理,再任务队列 |
特点
- JS 是单线程,同一时刻只做一件事
- 同步代码在调用栈中直接执行
- 异步操作的回调放入任务队列等待
- 事件循环负责在调用栈空时把队列里的任务拿出来执行
- 同步代码一定比异步回调先执行
理解
把整个执行机制理解成一家餐厅。
调用栈是厨房的灶台,一次只能炒一个菜(单线程)。
同步代码就是普通的菜,来了直接上灶台炒。
异步操作像是炖汤,交给旁边的慢炖锅(浏览器),不用占灶台。
汤炖好了,服务员把”汤好了”的纸条放到队列里(任务队列)。
事件循环就是厨房调度员:灶台空了,就看队列里有没有等着的纸条,有的话就把那道菜安排上灶台。
引出
理解了执行机制的基本流程后,可以继续学习 JS 事件循环,深入了解宏任务和微任务的区别。
也可以学习 JS 异步编程,了解异步代码的不同写法和演变过程。