JS执行机制:同步任务与异步任务的“先来后到“

243 阅读4分钟

JavaScript作为一门单线程语言,同一时间只能执行一个任务。如果某个任务耗时过长,会阻碍后续代码执行,导致页面卡顿甚至崩溃。本文将深入介绍同步任务和异步任务(宏任务和微任务).

同步任务:

在 JavaScript 中,同步任务(Synchronous Tasks) 是指按照代码书写顺序,立即执行的任务。它们是单线程的,会阻塞后续代码的执行,直到当前任务完成。


  1. 同步任务的特点
  • 立即执行:代码从上到下依次执行,前一个任务完成后才会执行下一个任务。
  • 阻塞性:如果某个同步任务耗时较长(如大量计算),会阻塞后续代码和 UI 渲染,导致页面卡顿。
  • 可预测性:执行顺序固定,与代码书写顺序一致。

  1. 常见的同步任务
  • 变量声明与赋值:
  let a = 1; // 同步任务
  console.log(a); // 同步任务
  • 函数调用:
  function foo() {
    console.log('foo');
  }
  foo(); // 同步调用

数学运算:

  let sum = 1 + 2; // 同步任务
  • 异常抛出:
  throw new Error('同步错误'); // 同步任务

  1. 同步任务 vs 异步任务

特性 同步任务 异步任务 执行时机 立即执行 延迟执行(等待某个条件触发) 是否阻塞 会阻塞后续代码 不会阻塞,通过回调/Promise 处理 示例 console.log()let a = 1 setTimeout()fetch()


  1. 同步任务的优缺点
  • 优点:
    • 简单直观,易于理解和调试。
    • 执行顺序固定,逻辑清晰。
  • 缺点:
    • 耗时操作会阻塞线程,影响用户体验。
    • 无法处理高并发或 I/O 密集型任务。

  1. 同步任务的执行顺序示例
console.log('1'); // 同步任务
console.log('2'); // 同步任务
console.log('3'); // 同步任务

输出顺序:

1
2
3

解析:代码从上到下依次执行,每个 console.log 都是同步任务,按顺序输出。


  1. 同步任务与事件循环
  • 同步任务属于当前执行栈(Call Stack) 中的任务。
  • 当执行栈为空时,事件循环才会检查微任务队列和宏任务队列。

异步任务、宏任务和微任务详解

  1. 异步任务(Asynchronous Task)
  • 定义:异步任务是指不阻塞主线程,在后台执行的任务。当异步任务完成时,会通过回调函数、Promise 或 async/await 等方式通知主线程处理结果。
  • 目的:避免程序因等待耗时操作(如网络请求、文件读写)而卡顿,提高程序性能和用户体验。
  • 实现方式:
    • 回调函数:如 setTimeout(callback, 1000)
    • Promise:如 fetch('/api').then(res => ...)
    • async/await:如 const data = await fetch('/api')
  1. 宏任务(Macrotask)
  • 定义:宏任务是异步任务的一种,由宿主环境(如浏览器或 Node.js)管理,按顺序执行。
  • 常见宏任务:
    • setTimeoutsetInterval 的回调函数。
    • I/O 操作(如文件读写、网络请求)的回调。
    • setImmediate(Node.js 环境)。
  • 执行时机:当前执行栈清空后,事件循环(Event Loop)从宏任务队列中取出任务执行。
  1. 微任务(Microtask)
  • 定义:微任务是异步任务的另一种,优先级高于宏任务,在当前宏任务执行完毕后立即执行。
  • 常见微任务:
    • Promise 的回调(.then/.catch/.finally)。
    • MutationObserver 的回调。
    • queueMicrotask 添加的任务。
  • 执行时机:当前宏任务执行完后,且在渲染之前,事件循环会清空微任务队列。
  1. 执行顺序示例
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. 同步代码直接执行,输出 14

  2. setTimeout 是宏任务,进入宏任务队列。

  3. Promise.then 是微任务,进入微任务队列。

  4. 当前宏任务(同步代码)执行完后,先清空微任务队列,输出 3

  5. 最后执行宏任务,输出 2

  6. 总结对比

特性 宏任务 微任务 执行时机 当前执行栈清空后,事件循环调度 当前宏任务执行完后立即执行 优先级 低 高 常见类型 setTimeout、I/O 操作 Promise、MutationObserver 是否阻塞渲染 是(执行完宏任务后可能渲染) 否(在渲染前执行完所有微任务)

  1. 应用场景
  • 宏任务:适合处理 I/O 密集型操作(如文件读写、网络请求),避免阻塞主线程。
  • 微任务:适合处理需要快速响应的操作(如状态更新、错误处理),确保在渲染前完成。
  1. 注意事项
  • 避免微任务无限循环:如果微任务中不断添加新的微任务,会阻塞宏任务的执行,导致页面卡死。
  • 合理使用宏任务和微任务:根据任务的优先级和性质,选择合适的异步任务类型,优化程序性能。

总结:

通过理解同步任务,异步任务,宏任务和微任务的概念和执行顺序,可以更好地编写高效、流畅的 JavaScript 程序。