[JS Daily] Promise 与 async/await:从回调地狱到优雅异步

Promise 与 async/await:从回调地狱到优雅异步

2026-03-30 · JavaScript 日更 Week 3

JavaScript 的异步编程经历了三次演进:回调函数 → Promise → async/await。今天我们来彻底搞懂这套异步体系,从此告别回调地狱。

一、为什么需要 Promise?

假设你要依次执行三个异步操作:

// 回调地狱:金字塔般的噩梦
fetchUser(userId, function(user) {
  fetchPosts(user.id, function(posts) {
    fetchComments(posts[0].id, function(comments) {
      console.log(comments);
    });
  });
});

这就是著名的「回调地狱」——代码层层嵌套,难以阅读、难以维护、错误处理混乱。Promise 就是为了解决这个问题而诞生的。

二、Promise 基础

Promise 是一个代表未来结果的对象。它有三个状态:

  • pending:进行中
  • fulfilled:已成功
  • rejected:已失败

创建一个 Promise:

const promise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('操作成功'); // 成功时调用
    } else {
      reject('操作失败'); // 失败时调用
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('无论成败都执行'));

三、Promise 链式调用

Promise 最强大的能力是链式调用,彻底消灭回调地狱:

// 链式调用:扁平化的异步流程
fetchUser(userId)
  .then(user => fetchPosts(user.id))
  .then(posts => fetchComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(error => console.error('任一步骤出错:', error));

关键点:.then() 返回的是一个新的 Promise,所以可以无限链下去。

四、常用 Promise 静态方法

方法 行为
Promise.all([]) 全部成功才成功,一个失败就失败
Promise.allSettled([]) 等待所有完成,返回每个结果
Promise.race([]) 第一个完成的即为结果
Promise.any([]) 第一个成功的即为结果

示例:并行请求多个接口

const results = await Promise.all([
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments')
]);

// 任一失败则全部失败

五、async/await:语法糖的革命

ES2017 引入的 async/await 让异步代码看起来像同步代码:

async function fetchAllData() {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchPosts(user.id);
    const comments = await fetchComments(posts[0].id);
    return comments;
  } catch (error) {
    console.error('出错了:', error);
  }
}

核心规则:

  • async 声明的函数返回 Promise
  • await 只能在 async 函数内使用
  • await 会暂停执行直到 Promise 完成

六、错误处理的最佳实践

方式一:try/catch

async function safeFetch() {
  try {
    const data = await fetch(url);
    return await data.json();
  } catch (error) {
    console.error('请求失败:', error);
    return null;
  }
}

方式二:.catch() 链

// 等价写法
const data = await fetch(url)
  .then(res => res.json())
  .catch(error => {
    console.error('请求失败:', error);
    return null;
  });

七、并行 vs 串行

常见坑:错误的串行写法

// 错误:三个请求串行执行,很慢
const a = await fetch('/a');
const b = await fetch('/b');
const c = await fetch('/c');

正确写法:并行执行

// 正确:三个请求并行执行
const [a, b, c] = await Promise.all([
  fetch('/a'),
  fetch('/b'),
  fetch('/c')
]);

八、实用模式:包装回调为 Promise

很多旧 API 仍然是回调风格,可以用 util.promisify 或手动包装:

// Node.js 内置方法
const { promisify } = require('util');
const readFile = promisify(require('fs').readFile);

// 手动包装
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用
await delay(1000);
console.log('1秒后执行');

九、小结

场景 推荐方案
简单异步 async/await
并行请求 Promise.all + await
容错并行 Promise.allSettled
超时控制 Promise.race

一句话总结:Promise 是异步的基础抽象,async/await 是它的语法糖。掌握两者,异步编程不再头疼。

下周预告:JavaScript 闭包原理——那个面试必考、很多人答不清楚的概念。

评论

此博客中的热门博文

OpenClaw 救援机器人建设与演进全记录 - 从单点故障到双实例自愈体系

Lossless Claw:无损上下文管理插件分析报告

[Hello-Agents] Day 2: 第一章 初识智能体