同步、异步、Promise、then、async/await

一、同步与异步的本质区别

同步和异步是编程中两种不同的执行模式,理解它们的区别是掌握异步编程的基础。

同步(Synchronous):代码按照书写顺序依次执行,前一个操作完成才能进行下一个操作。例如:

javascript 复制代码
console.log('第一行');
console.log('第二行'); 
// 输出顺序永远是"第一行"然后"第二行"

异步(Asynchronous):代码的执行不按照书写顺序,某些操作会被放入任务队列,等待主线程空闲时执行。例如:

javascript 复制代码
console.log('开始');
setTimeout(() => console.log('定时器回调'), 0);
console.log('结束');
// 输出顺序: 开始 -> 结束 -> 定时器回调

异步操作常见于:

  • 网络请求(AJAX/fetch)
  • 定时器(setTimeout/setInterval)
  • 文件读写(Node.js)
  • 数据库操作

关键区别:同步会阻塞代码执行,异步不会阻塞后续代码的执行。

二、回调地狱与Promise的诞生

回调地狱问题

在Promise出现之前,异步操作主要依赖回调函数,多层嵌套会导致"回调地狱":

javascript 复制代码
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      // 更多嵌套...
    });
  });
});

这种代码难以阅读、调试和维护,错误处理也非常复杂。

Promise的解决方案

Promise是ES6引入的异步编程解决方案,它通过链式调用解决了回调地狱问题。

Promise三大状态

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

状态一旦改变就不可逆(Pending → Fulfilled 或 Pending → Rejected。

基本用法

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 成功 */) {
    resolve(value); // 状态变为Fulfilled
  } else {
    reject(error);  // 状态变为Rejected
  }
});

promise.then(
  value => { /* 成功处理 */ },
  error => { /* 失败处理 */ }
);

链式调用示例

javascript 复制代码
function p1() {
  return new Promise(resolve => {
    setTimeout(() => resolve('结果1'), 1000);
  });
}

function p2(data) {
  return new Promise(resolve => {
    setTimeout(() => resolve(data + ' 结果2'), 1000);
  });
}

p1()
  .then(p2)
  .then(result => console.log(result))
  .catch(error => console.error(error));
// 2秒后输出: "结果1 结果2"

Promise的链式调用使异步代码有了同步代码的阅读体验。

三、async/await:Promise的语法糖

async/await是ES2017引入的语法,基于Promise实现,但代码更加简洁。

async函数

  • async函数总是返回一个Promise对象
  • 如果返回值不是Promise,会自动包装成Promise
javascript 复制代码
async function foo() {
  return 'hello';
}
// 等价于
function foo() {
  return Promise.resolve('hello');
}

await表达式

  • await后面通常是一个Promise对象
  • 会"暂停"async函数的执行,等待Promise解决
  • 如果是rejected状态,会抛出异常
javascript 复制代码
async function getData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('获取数据失败:', error);
  }
}

async/await与Promise的关系

  1. async函数返回的是Promise对象
  2. await后面可以接任何thenable对象(有then方法的对象)
  3. async/await底层是基于Promise和生成器实现的语法糖

对比示例

Promise版本:

javascript 复制代码
function loadData() {
  return fetchData()
    .then(data => processData(data))
    .then(result => displayResult(result))
    .catch(error => handleError(error));
}

async/await版本:

javascript 复制代码
async function loadData() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    displayResult(result);
  } catch (error) {
    handleError(error);
  }
}

async/await让异步代码看起来更像同步代码,提高了可读性。

四、错误处理机制对比

Promise的错误处理

  1. 使用.catch()捕获错误:
javascript 复制代码
somePromise()
  .then(handleSuccess)
  .catch(handleError);
  1. 或者在then中传入第二个参数:
javascript 复制代码
somePromise().then(
  handleSuccess,
  handleError
);

async/await的错误处理

使用try/catch结构:

javascript 复制代码
async function foo() {
  try {
    const result = await somePromise();
    // 处理结果
  } catch (error) {
    // 处理错误
  }
}

五、实际应用场景

适合Promise的场景

  1. 需要并行执行多个异步操作时:
javascript 复制代码
Promise.all([asyncTask1(), asyncTask2(), asyncTask3()])
  .then(([result1, result2, result3]) => {
    // 所有任务都完成后的处理
  });
  1. 需要竞速执行多个异步操作时:
javascript 复制代码
Promise.race([fetch1, fetch2])
  .then(firstResult => {
    // 使用最先返回的结果
  });

适合async/await的场景

  1. 有前后依赖关系的异步操作:
javascript 复制代码
async function processUserData(userId) {
  const user = await getUser(userId);
  const orders = await getOrders(user.id);
  const payments = await getPayments(user.id);
  return { user, orders, payments };
}
  1. 需要更清晰错误处理的复杂异步流程。

六、性能考量

  1. Promise创建后会立即执行,无法取消
  2. async/await不会带来额外性能开销,它只是语法糖
  3. 过度使用await会导致不必要的等待:
javascript 复制代码
// 不推荐 - 顺序执行
const a = await getA();
const b = await getB(); 

// 推荐 - 并行执行
const [a, b] = await Promise.all([getA(), getB()]);

七、总结与最佳实践

  1. 演进历程:回调 → Promise → async/await
  2. 关系
    • Promise是async/await的基础
    • async函数返回Promise
    • await等待Promise解决
  3. 最佳实践
    • 简单异步操作可直接使用Promise
    • 复杂异步流程推荐async/await
    • 并行无依赖任务使用Promise.all
    • 始终处理错误(.catch或try/catch)
  4. 注意事项
    • 避免在循环中错误使用await
    • 合理使用Promise缓存避免重复请求
    • 在Node.js中注意Promise的内存泄漏问题

通过合理结合Promise和async/await,可以编写出既高效又易于维护的异步JavaScript代码。

相关推荐
NiceCloud喜云18 分钟前
Opus 4.8 的 Effort Control 怎么选:Low 到 Max 五档策略
android·java·大数据·前端·c++·python·spring
wordbaby1 小时前
React Native + RNOH:跨页面数据回传的最佳实践与避坑指南
前端·react native
GISer_Jing1 小时前
Three.js着色器编译机制深度解析
javascript·webgl·着色器
丷丩1 小时前
MapLibre GL JS第22课:查看本地GeoJSON
前端·javascript·map·mapbox·maplibre gl js
油炸自行车1 小时前
Claude Code 错误:API Error: 400 Failed to deserialize the JSON body into the
开发语言·javascript·json·trae·claude code·api error 400
Front思2 小时前
AI前端工程师需要具备能力+
前端·人工智能·ai
ZC跨境爬虫4 小时前
跟着 MDN 学CSS day_29:(掌握文本与字体样式的核心艺术)
前端·css·ui·html·tensorflow
李子琪。5 小时前
网络空间安全深度实战:CSRF 漏洞原理剖析与基于 Token 的纵深防御体系构建(全栈实验报告)
前端·安全·csrf
冰暮流星5 小时前
javascript之history对象介绍
前端·笔记
IT_陈寒5 小时前
Vite热更新失灵?你可能漏了这个配置
前端·人工智能·后端