JavaScript作为一门单线程语言,同一时间只能执行一个任务。如果某个任务耗时过长,会阻碍后续代码执行,导致页面卡顿甚至崩溃。本文将深入介绍同步任务和异步任务(宏任务和微任务).
同步任务:
在 JavaScript 中,同步任务(Synchronous Tasks) 是指按照代码书写顺序,立即执行的任务。它们是单线程的,会阻塞后续代码的执行,直到当前任务完成。
- 同步任务的特点
- 立即执行:代码从上到下依次执行,前一个任务完成后才会执行下一个任务。
- 阻塞性:如果某个同步任务耗时较长(如大量计算),会阻塞后续代码和 UI 渲染,导致页面卡顿。
- 可预测性:执行顺序固定,与代码书写顺序一致。
- 常见的同步任务
- 变量声明与赋值:
let a = 1; // 同步任务
console.log(a); // 同步任务
- 函数调用:
function foo() {
console.log('foo');
}
foo(); // 同步调用
数学运算:
let sum = 1 + 2; // 同步任务
- 异常抛出:
throw new Error('同步错误'); // 同步任务
- 同步任务 vs 异步任务
特性 同步任务 异步任务
执行时机 立即执行 延迟执行(等待某个条件触发)
是否阻塞 会阻塞后续代码 不会阻塞,通过回调/Promise 处理
示例 console.log()、let a = 1 setTimeout()、fetch()
- 同步任务的优缺点
- 优点:
- 简单直观,易于理解和调试。
- 执行顺序固定,逻辑清晰。
- 缺点:
- 耗时操作会阻塞线程,影响用户体验。
- 无法处理高并发或 I/O 密集型任务。
- 同步任务的执行顺序示例
console.log('1'); // 同步任务
console.log('2'); // 同步任务
console.log('3'); // 同步任务
输出顺序:
1
2
3
解析:代码从上到下依次执行,每个 console.log 都是同步任务,按顺序输出。
- 同步任务与事件循环
- 同步任务属于当前执行栈(Call Stack) 中的任务。
- 当执行栈为空时,事件循环才会检查微任务队列和宏任务队列。
异步任务、宏任务和微任务详解
- 异步任务(Asynchronous Task)
- 定义:异步任务是指不阻塞主线程,在后台执行的任务。当异步任务完成时,会通过回调函数、Promise 或 async/await 等方式通知主线程处理结果。
- 目的:避免程序因等待耗时操作(如网络请求、文件读写)而卡顿,提高程序性能和用户体验。
- 实现方式:
- 回调函数:如
setTimeout(callback, 1000)。 - Promise:如
fetch('/api').then(res => ...)。 - async/await:如
const data = await fetch('/api')。
- 回调函数:如
- 宏任务(Macrotask)
- 定义:宏任务是异步任务的一种,由宿主环境(如浏览器或 Node.js)管理,按顺序执行。
- 常见宏任务:
setTimeout、setInterval的回调函数。- I/O 操作(如文件读写、网络请求)的回调。
setImmediate(Node.js 环境)。
- 执行时机:当前执行栈清空后,事件循环(Event Loop)从宏任务队列中取出任务执行。
- 微任务(Microtask)
- 定义:微任务是异步任务的另一种,优先级高于宏任务,在当前宏任务执行完毕后立即执行。
- 常见微任务:
Promise的回调(.then/.catch/.finally)。MutationObserver的回调。queueMicrotask添加的任务。
- 执行时机:当前宏任务执行完后,且在渲染之前,事件循环会清空微任务队列。
- 执行顺序示例
console.log('1. 同步代码');
setTimeout(() => {
console.log('2. 宏任务(setTimeout)');
}, 0);
Promise.resolve().then(() => {
console.log('3. 微任务(Promise)');
});
console.log('4. 同步代码');
输出顺序:
1. 同步代码
4. 同步代码
3. 微任务(Promise)
2. 宏任务(setTimeout)
解析:
-
同步代码直接执行,输出
1和4。 -
setTimeout是宏任务,进入宏任务队列。 -
Promise.then是微任务,进入微任务队列。 -
当前宏任务(同步代码)执行完后,先清空微任务队列,输出
3。 -
最后执行宏任务,输出
2。 -
总结对比
特性 宏任务 微任务
执行时机 当前执行栈清空后,事件循环调度 当前宏任务执行完后立即执行
优先级 低 高
常见类型 setTimeout、I/O 操作 Promise、MutationObserver
是否阻塞渲染 是(执行完宏任务后可能渲染) 否(在渲染前执行完所有微任务)
- 应用场景
- 宏任务:适合处理 I/O 密集型操作(如文件读写、网络请求),避免阻塞主线程。
- 微任务:适合处理需要快速响应的操作(如状态更新、错误处理),确保在渲染前完成。
- 注意事项
- 避免微任务无限循环:如果微任务中不断添加新的微任务,会阻塞宏任务的执行,导致页面卡死。
- 合理使用宏任务和微任务:根据任务的优先级和性质,选择合适的异步任务类型,优化程序性能。
总结:
通过理解同步任务,异步任务,宏任务和微任务的概念和执行顺序,可以更好地编写高效、流畅的 JavaScript 程序。