同步、异步、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代码。

相关推荐
京东云开发者31 分钟前
全球首个!京东全栈开源JoyAI-VL-Interaction,让大模型从“一问一答”走向“边看边说”
前端
京东云开发者31 分钟前
正式上线!京东云AI智能渗透测试服务
前端
zzzzzz31035 分钟前
当甲方说'logo放大的同时再缩小一点'时,我用 AI 把这个需求做出来了
javascript·css·程序员
AprChell35 分钟前
低代码设计器和低代码设计引擎架构综述
前端·vue.js·低代码
Ruihong36 分钟前
🎉 VuReact 1.9.0 发布,支持 Vue 3.4 defineModel 编译到 React
vue.js·react.js·面试
Hilaku41 分钟前
Node.js 还能再战十年?给你一个不换引擎的理由
前端·javascript·程序员
颜进强1 小时前
AI性能参数-截断、延迟与流式输出
前端·后端·ai编程
spmcor1 小时前
React 架构师之路:Next.js 全栈革命(第八篇)
前端·react.js
英勇无比的消炎药1 小时前
TinyRobot 源码深度分析:OpenTiny 的 AI 对话组件库
前端·vue.js·github
假如让我当三天老蒯1 小时前
React基础、进阶(学习用)
前端·react.js·面试