前言

JavaScript 是单线程语言,同一时间只能做一件事。如果所有代码都同步顺序执行,遇到网络请求、定时器、文件读取这类耗时操作,页面就会卡死阻塞,用户体验极差。异步编程就是为了解决单线程阻塞问题而生,是前端开发核心必备技能,也是面试高频考点。

本文从零讲解 JS 异步发展历程、核心原理、用法实战。

一、同步与异步核心区别

1. 同步执行

代码从上到下依次执行,上一行没执行完,下一行永远等待。

js

console.log("1");
console.log("2");
console.log("3");

输出顺序:1 → 2 → 3,顺序固定,依次执行

2. 异步执行

耗时任务交给浏览器后台执行,主线程继续往下走,等任务完成再回头执行回调。

js

console.log("1");
setTimeout(() => {
  console.log("2");
}, 1000);
console.log("3");

输出顺序:1 → 3 → 2定时器延时任务异步执行,不会阻塞主线程代码运行。

二、JS 异步发展四大阶段

阶段 1:回调函数(最原始异步)

原理

把耗时操作完成后要执行的代码,放进函数里当作参数传递,任务结束自动调用。

实战:定时器回调

js

// 延时2秒执行
setTimeout(function () {
  console.log("异步任务执行完成");
}, 2000);
实战:模拟接口请求回调

js

// 模拟获取用户数据
function getUserInfo(callback) {
  setTimeout(() => {
    let user = { name: "前端小白", age: 22 };
    callback(user); // 执行回调
  }, 1500);
}

// 调用
getUserInfo((res) => {
  console.log("拿到用户数据:", res);
});
缺点:回调地狱

多层异步嵌套,代码层层缩进,可读性极差、极难维护:

js

setTimeout(() => {
  console.log("第一层");
  setTimeout(() => {
    console.log("第二层");
    setTimeout(() => {
      console.log("第三层");
    }, 1000);
  }, 1000);
}, 1000);

代码臃肿、无法统一捕获错误,正式项目禁止大量嵌套


阶段 2:Promise(ES6 标准异步方案)

核心作用

解决回调地狱,把横向嵌套代码改成纵向链式调用,统一异步状态管理。

Promise 三种状态
  1. pending 进行中(初始状态)
  2. fulfilled 成功
  3. rejected 失败状态一旦改变,永久无法更改
基础语法

js

// 创建Promise实例
let p = new Promise((resolve, reject) => {
  // 异步耗时操作
  let flag = true;
  if (flag) {
    resolve("请求成功数据"); // 成功调用
  } else {
    reject("请求失败"); // 失败调用
  }
});

// 接收结果
p.then(res => {
  console.log("成功:", res);
}).catch(err => {
  console.log("失败:", err);
})
链式调用(解决回调地狱)

js

function fn1() {
  return new Promise(resolve => {
    setTimeout(() => resolve("第一步完成"), 1000);
  });
}
function fn2() {
  return new Promise(resolve => {
    setTimeout(() => resolve("第二步完成"), 1000);
  });
}

// 纵向流畅执行
fn1()
.then(res => {
  console.log(res);
  return fn2();
})
.then(res => {
  console.log(res);
})
常用静态方法

1. Promise.all() 全部成功才成功,适合并发请求

js

let p1 = Promise.resolve("接口1");
let p2 = Promise.resolve("接口2");

Promise.all([p1, p2]).then(res => {
  console.log("全部请求完成", res);
});

2. Promise.race () 竞速执行

规则:多个 Promise 同时执行,谁最先结束就返回谁的结果,其余任务继续执行但结果会被忽略。常用场景:接口请求超时拦截、竞速请求择优使用。

js

// 模拟两个不同耗时的异步任务
const task1 = new Promise((resolve) => {
  setTimeout(() => resolve("任务1完成"), 2000);
});
const task2 = new Promise((resolve) => {
  setTimeout(() => resolve("任务2完成"), 1000);
});

// 竞速执行
Promise.race([task1, task2])
.then(res => {
  // 只会输出先完成的任务2
  console.log("最先完成:", res);
})

实战超时请求案例(工作常用)

js

// 模拟接口请求
function requestData() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("接口数据"), 3000);
  })
}

// 超时限制
function timeOut() {
  return new Promise((_, reject) => {
    setTimeout(() => reject("请求超时,请重试"), 2000);
  })
}

// 竞速:2秒内没请求成功直接判定超时
Promise.race([requestData(), timeOut()])
.then(res => console.log(res))
.catch(err => console.log(err))
  1. Promise.allSettled() 不管成功失败,统一获取所有结果
  2. Promise.resolve() 快速创建成功 Promise
  3. Promise.reject() 快速创建失败 Promise

阶段 3:async /await(ES7 优雅异步)

核心优势

以同步写法,实现异步逻辑,代码最简洁、可读性最高,企业项目主流用法。

使用规则
  1. await 只能放在 async 修饰的函数内部
  2. await 后面必须跟 Promise 对象
  3. 代码会暂停等待异步执行完毕,再往下走
基础实战

js

// 封装异步请求函数
function getMsg() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("后端返回数据");
    }, 1500);
  });
}

// async 声明异步函数
async function main() {
  console.log("开始请求");
  // 等待异步完成
  let result = await getMsg();
  console.log("拿到数据:", result);
  console.log("后续逻辑执行");
}

// 调用
main();
异常捕获(必学)

异步请求大概率失败,用 try/catch 捕获错误:

js

async function getData() {
  try {
    let res = await getMsg();
    console.log("成功", res);
  } catch (err) {
    console.log("请求出错:", err);
  }
}
getData();

三、JS 异步底层核心:事件循环 EventLoop

通俗讲解

  1. JS 主线程执行同步代码,执行完清空执行栈
  2. 异步任务放入任务队列排队
  3. 同步代码全部执行完毕,再从队列取出异步任务执行

任务分类

  1. 微任务:优先级更高(Promise.then、async/await)
  2. 宏任务:优先级较低(setTimeout、ajax、DOM 事件)执行顺序:同步代码 → 所有微任务 → 所有宏任务

经典面试题小白版

js

console.log("同步1");
setTimeout(() => {
  console.log("宏任务");
}, 0);
Promise.resolve().then(() => {
  console.log("微任务");
});
console.log("同步2");

输出顺序:同步 1 → 同步 2 → 微任务 → 宏任务

四、实战场景总结

  1. 定时器延时操作:异步基础用法
  2. Ajax / Fetch / Axios 后端接口请求:全用 async+await
  3. 文件读写、图片预加载、页面异步渲染
  4. 多个接口按顺序先后请求
  5. 接口超时拦截:优先使用 Promise.race

五、学习路线总结

  1. 先懂同步异步概念,分清阻塞与非阻塞
  2. 掌握回调函数,理解异步底层逻辑
  3. 精通Promise 状态与所有静态方法
  4. 主力使用 async / await 写项目业务
  5. 吃透事件循环,搞定所有异步面试题

结尾寄语

JS 异步编程是前端从入门到进阶的分水岭,学会异步才算真正懂 JavaScript。摒弃老旧回调嵌套,熟练使用 Promise + async/await,既能写出优雅简洁代码,也能轻松应对求职面试,快速提升职场核心竞争力。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐