news 2026/5/28 12:10:03

JavaScript的同步与异步

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript的同步与异步

一、开篇:为什么 JS 需要同步与异步?

JavaScript 作为浏览器和 Node.js 的核心脚本语言,单线程是其天生特性 —— 同一时间只能执行一段代码。这一设计源于 JS 的核心用途:处理页面交互(DOM 操作)和网络请求,若所有操作都同步执行,一个耗时的网络请求就会导致页面卡死(俗称 “阻塞”)。

比如:

// 同步代码的阻塞问题

function syncTask() {

let start = Date.now();

while (Date.now() - start 0) {} // 模拟3秒耗时操作

console.log("同步任务完成");

}

syncTask();

console.log("后续代码"); // 必须等待3秒后才执行

此时页面会卡顿 3 秒,用户无法点击、滚动。而异步机制的出现,正是为了解决 “单线程阻塞” 问题,让 JS 能在等待耗时操作时,继续执行其他任务。

二、核心概念:同步与异步的本质区别

1. 同步(Synchronous)
  • 定义:代码按顺序执行,前一个任务完成后,才执行后一个任务,任务执行期间会阻塞线程。
  • 特点:顺序执行、阻塞线程、结果即时返回。
  • 常见场景:基本运算(加减乘除)、变量赋值、普通函数调用、for 循环等。

示例:

let a = 1;

let b = 2;

let c = a + b; // 同步执行,立即得到结果3

console.log(c); // 顺序输出3

2. 异步(Asynchronous)
  • 定义:任务启动后不等待其完成,直接执行后续任务,耗时任务在后台完成后,通过回调 / Promise 等方式通知并执行结果处理。
  • 特点:非顺序执行、不阻塞线程、结果延迟返回。
  • 常见场景:网络请求(AJAX/fetch)、定时器(setTimeout/setInterval)、文件读写(Node.js)、DOM 事件监听(click/load)等。

示例:

console.log("1. 启动异步任务");

setTimeout(() => {

console.log("3. 异步任务完成"); // 延迟1秒执行

}, 1000);

console.log("2. 继续执行后续代码"); // 不等待异步任务,立即执行

// 输出顺序:1 → 2 → 3

三、底层原理:JS 的异步执行机制

要彻底理解同步异步,必须搞懂调用栈(Call Stack)、任务队列(Task Queue)、事件循环(Event Loop)这三大核心组件。

1. 核心组件分工
  • 调用栈:负责执行同步代码,遵循 “先进后出” 原则。同步任务执行时入栈,执行完毕后出栈。
  • 任务队列:存放异步任务的回调函数,分为 “宏任务队列” 和 “微任务队列”(优先级:微任务 > 宏任务)。
    • 宏任务:setTimeout、setInterval、DOM 事件、AJAX 请求、script 标签执行。
    • 微任务:Promise.then/catch/finally、async/await、process.nextTick(Node.js,优先级最高)。
  • 事件循环:持续监控调用栈和任务队列,当调用栈为空时,将任务队列中的回调函数压入调用栈执行。
2. 异步执行流程(关键!)
  1. 执行同步代码,同步任务依次入栈、出栈。
  1. 遇到异步任务时,启动异步操作(如定时器计时、网络请求),并将回调函数注册到对应任务队列。
  1. 同步代码执行完毕(调用栈为空),事件循环开始工作。
  1. 优先清空微任务队列:将微任务队列中所有回调函数依次压入调用栈执行,直到微任务队列为空。
  1. 再从宏任务队列中取出第一个回调函数,压入调用栈执行。
  1. 重复步骤 3-5,形成循环。
3. 经典案例:验证执行顺序

console.log("1. 同步代码开始");

setTimeout(() => {

console.log("6. 宏任务:setTimeout回调");

}, 0);

Promise.resolve().then(() => {

console.log("4. 微任务:Promise.then");

}).then(() => {

console.log("5. 微任务:第二个then");

});

console.log("2. 同步代码中间");

async function asyncTask() {

console.log("3. 同步:async函数执行");

await Promise.resolve(); // await后相当于微任务

console.log("7. 微任务:await后续代码");

}

asyncTask();

console.log("8. 同步代码结束");

执行顺序解析

  1. 同步代码入栈:输出 “1”→“2”→“3”→“8”(调用栈为空)。
  1. 事件循环触发,先处理微任务队列:
    • 第一个 Promise.then:输出 “4”,第二个 then 入微任务队列。
    • 第二个 Promise.then:输出 “5”,微任务队列暂空。
    • await 对应的微任务:输出 “7”,微任务队列彻底清空。
  1. 处理宏任务队列:取出 setTimeout 回调,输出 “6”。
  1. 最终输出:1 → 2 → 3 → 8 → 4 → 5 → 7 → 6。

四、异步编程的演进:从回调到 async/await

JS 异步编程经历了三次重要演进,核心目标是解决 “回调地狱”,让代码更易读、易维护。

1. 第一代:回调函数(Callback)
  • 特点:直接使用回调函数处理异步结果,简单直观但易产生 “回调地狱”。
  • 问题:嵌套层级深、代码可读性差、错误处理困难。

示例(回调地狱):

// 模拟获取用户信息→获取用户订单→获取订单详情

getUserInfo(userId, (userErr, userData) => {

if (userErr) throw userErr;

getOrderList(userData.id, (orderErr, orderData) => {

if (orderErr) throw orderErr;

getOrderDetail(orderData[0].id, (detailErr, detailData) => {

if (detailErr) throw detailErr;

console.log("订单详情:", detailData);

});

});

});

2. 第二代:Promise
  • 特点:用链式调用(.then ())替代嵌套回调,解决回调地狱,统一错误处理(.catch ())。
  • 核心状态:pending(进行中)→ fulfilled(成功)/rejected(失败),状态一旦改变不可逆转。

示例(Promise 链式调用):

getUserInfo(userId)

.then(userData => getOrderList(userData.id))

.then(orderData => getOrderDetail(orderData[0].id))

.then(detailData => console.log("订单详情:", detailData))

.catch(err => console.error("错误:", err));

3. 第三代:async/await
  • 特点:ES7 语法,基于 Promise,用同步的写法编写异步代码,可读性最强,是当前最优异步方案。
  • 核心规则:
    • async 函数返回值是 Promise 对象。
    • await 只能在 async 函数中使用,后面跟 Promise 对象,会暂停函数执行,等待 Promise 决议后继续。
    • 错误处理用 try/catch,比 Promise.catch 更直观。

示例(async/await 优化):

async function getOrderInfo(userId) {

try {

const userData = await getUserInfo(userId); // 等待Promise成功

const orderData = await getOrderList(userData.id);

const detailData = await getOrderDetail(orderData[0].id);

console.log("订单详情:", detailData);

} catch (err) {

console.error("错误:", err); // 统一捕获所有错误

}

}

getOrderInfo(userId);

五、实战避坑:95 分必备的关键知识点

1. setTimeout 的 “延迟” 不是绝对的

setTimeout 的延迟时间是 “最小延迟”,而非 “精确延迟”。因为回调函数需等待调用栈为空、微任务清空后才执行。

示例:

setTimeout(() => {

console.log("延迟1秒执行?");

}, 1000);

// 同步代码阻塞3秒

let start = Date.now();

while (Date.now() - start 0) {}

// 实际延迟3秒才执行,而非1秒

2. async/await 的本质是 Promise 语法糖

await 后的代码会被包装成微任务,且 await 会暂停当前 async 函数,而非整个线程。

示例:

async function test() {

console.log("1");

await Promise.resolve();

console.log("3"); // 微任务

}

test();

console.log("2"); // 同步代码,不被await阻塞

// 输出:1 → 2 → 3

3. 事件循环的浏览器与 Node.js 差异
  • 浏览器:微任务队列优先级高于宏任务队列,微任务执行完再执行宏任务。
  • Node.js(v11+):与浏览器一致;v11 前:先执行完一个宏任务,再执行所有微任务。
4. 避免异步代码中的常见错误
  • 不要在 for 循环中使用 var 声明变量(变量提升导致异步回调获取错误值),改用 let/const。
  • 不要忽略 Promise 错误(未写.catch () 会导致未捕获异常)。
  • 避免过度嵌套 async/await(可用 Promise.all 并行执行多个独立异步任务,提升性能)。

示例(Promise.all 并行优化):

// 串行执行:总耗时 = 1秒 + 2秒 = 3秒

async function serialTask() {

const res1 = await fetch("/api/1");

const res2 = await fetch("/api/2");

}

// 并行执行:总耗时 = 2秒(取最长任务时间)

async function parallelTask() {

const promise1 = fetch("/api/1");

const promise2 = fetch("/api/2");

const [res1, res2] = await Promise.all([promise1, promise2]);

}

六、总结:同步与异步的核心逻辑

JS 的同步异步本质是单线程模型下的效率优化方案

  • 同步保证代码执行的顺序性和确定性,适用于简单、无耗时的操作。
  • 异步通过 “任务队列 + 事件循环” 实现非阻塞执行,适用于耗时操作(网络、IO 等)。
  • 异步编程的演进(回调→Promise→async/await),核心是 “降低复杂度、提升可读性”。

掌握本文的原理、案例和避坑点,你就能轻松应对面试中的同步异步问题,以及实际开发中的异步场景优化啦!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/28 12:09:48

小白学Python避坑指南:这些错误90%的新手都会犯

前言Python 以其简洁易读的语法,成为了众多新手踏入编程世界的首选语言。然而,即使是看似简单的 Python,在学习过程中也隐藏着许多容易让人犯错的“陷阱”。据统计,90% 的新手在学习 Python 时都会遇到一些常见的错误。本文将为小…

作者头像 李华
网站建设 2026/5/26 17:01:24

基于单片机数字电子钟数码管显示系统Proteus仿真(含全部资料)

全套资料包含:Proteus仿真源文件keil C语言源程序AD原理图流程图元器件清单说明书等 资料下载: 通过网盘分享的文件:资料分享 链接: 百度网盘 请输入提取码 提取码: tgnu 目录 资料下载: Proteus仿真功能 项目文件资料&#…

作者头像 李华
网站建设 2026/5/23 6:40:21

Packet Tracer中交换机远程管理配置指南

从零开始掌握交换机远程管理:Packet Tracer实战全解析你有没有遇到过这样的场景?机房里几十台交换机层层堆叠,每次配置都要插线、开终端、敲命令……一旦设备分布在不同楼层或园区,运维效率直接“断崖式”下滑。这就是为什么远程管…

作者头像 李华
网站建设 2026/5/20 14:52:52

彻底解决Multisim主数据库路径错误的系统级配置流程

彻底解决Multisim主数据库路径错误的系统级修复实战你有没有遇到过这样的情况:刚重装完系统,兴冲冲打开Multisim准备做仿真,结果软件卡在启动界面,弹出一个冷冰冰的提示——“Database not found”?或者更糟&#xff0…

作者头像 李华
网站建设 2026/5/21 17:00:09

别再乱买电竞耳机了!职业选手都在用的“隐藏参数”曝光

花几百上千块买的电竞耳机,却总在决赛圈被敌人绕后偷袭?明明开着7.1环绕声,却连脚步声从左还是右来都分不清?团战沟通时,队友只听见你这边的键盘杂音,关键指令全错过?别再怪自己反应慢&#xff…

作者头像 李华