Promise 从入门到精通

编程 > WEB > JavaScript (196) 2026-08-12 10:32:21

需求

 

作为前端开发,异步编程是我们每天都要面对的。而 Promise 就是解决异步问题的“终极武器”。无论你是刚入门,还是已经用过 async/await,搞懂 Promise 的原理和用法,都能让你写代码更自信、更优雅。

今天我们就用最直白的语言 + 最实用的代码,把 Promise 彻底讲透!

 

1. 为什么需要 Promise

先看一段“回调地狱”经典代码:

getUser((user) => {
  getOrders(user.id, (orders) => {
    getOrderDetails(orders[0].id, (detail) => {
      console.log(detail);
    });
  });
});

层层嵌套,可读性差,难以维护,更别说错误处理了。Promise 就是为了解决这个问题而生的——它让异步操作变得像同步一样线性、可组合。

 

2. Promise 是什么

 

一个 Promise 对象代表一个异步操作的最终完成(或失败)及其结果值。

你可以把它想象成一个“承诺”:现在我还不能给你结果,但将来一定会告诉你(成功或失败)。

 

三个状态(生命周期)

 

  • pending(进行中)——初始状态
  • fulfilled(已成功)——操作成功,resolve() 被调用
  • rejected(已失败)——操作失败,reject() 被调用

 

一旦状态从 pending 变为 fulfilled 或 rejected,就不可再变。

 

3. 基本用法:创建 Promise 并消费

 

创建一个 Promise

const myPromise = new Promise((resolve, reject) => {
  // 执行异步任务(如 ajax、setTimeout、读取文件等)
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('数据加载成功 🎉');
    } else {
      reject('加载失败 😢');
    }
  }, 1000);
});

 

消费 Promise:.then() 和 .catch()

 

myPromise
  .then((result) => {
    console.log(result); // 1秒后输出:数据加载成功 🎉
  })
  .catch((error) => {
    console.error(error);
  });

 

 

4. 链式调用 —— 串行异步任务

 

这是 Promise 最强大的地方:

function getUser() {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id: 1, name: '张三' }), 500);
  });
}
function getOrders(userId) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(['订单A', '订单B']), 500);
  });
}
getUser()
  .then((user) => {
    console.log(user); // { id: 1, name: '张三' }
    return getOrders(user.id); // 返回新的 Promise
  })
  .then((orders) => {
    console.log(orders); // ['订单A', '订单B']
  })
  .catch((err) => {
    console.error('出错了:', err);
  });

注意:.then() 中返回一个值,会被包装成 resolved Promise;返回另一个 Promise,会等待其完成。

 

5. 错误处理 —— 捕获所有异常

 

链式中的 .catch() 可以捕获前面任何一个环节抛出的错误,非常方便。

getUser()
  .then((user) => {
    if (!user) throw new Error('用户不存在');
    return getOrders(user.id);
  })
  .then((orders) => {
    // 如果订单为空,主动抛错
    if (orders.length === 0) throw new Error('没有订单');
    console.log(orders);
  })
  .catch((err) => {
    console.error('捕获到错误:', err.message);
  });

如果不用 .catch(),错误会被“吞掉”,所以永远别忘了加 catch!

 

 

6. 常用静态方法

 

清单

方法 作用 是否等待所有?
Promise.resolve() 立即返回成功状态的 Promise ❌
Promise.reject() 立即返回失败状态的 Promise ❌
Promise.all() 全部成功才成功,一个失败就失败 ✅(并发)
Promise.allSettled() 等待全部完成(无论成败) ✅(并发)
Promise.race() 谁先完成就返回谁 ✅(并发)
Promise.any() 一个成功就返回,全部失败才失败 ✅(并发)

 

Promise.resolve(value)  快速创建一个状态已定的 Promise 对象

 

作用:将给定的值包装成一个 成功(fulfilled) 状态的 Promise。

Promise.resolve(value);

等价于:

new Promise((resolve) => resolve(value));

什么时候用?

  • 当你有一个值,但后面的代码期望得到一个 Promise 时(统一接口)。
  • 在拦截器或 .then 回调中,如果你想确保返回的一定是一个 Promise。

示例

// 场景:缓存里取数据
function getUser() {
  const cache = { name: '张三' };
  if (cache) {
    // 直接返回缓存数据,但为了统一,包装成 Promise
    return Promise.resolve(cache);
  } else {
    return fetch('/api/user');
  }
}
getUser().then(user => console.log(user)); // 无论哪种情况,都能用 .then

 

Promise.reject(reason) 

作用:返回一个 失败(rejected) 状态的 Promise,并携带错误原因 reason。

Promise.reject(reason);

等价于:

new Promise((resolve, reject) => reject(reason));

什么时候用?

  • 在拦截器或函数中,检测到错误条件,希望提前终止后续的 Promise 链,并进入 .catch。
  • 你代码中的 return Promise.reject(error) 就是最标准的用法。

例子:

function validateUser(user) {
  if (!user || !user.id) {
    // 参数校验失败,直接返回失败状态的 Promise,跳到 catch
    return Promise.reject(new Error('用户信息无效'));
  }
  return fetch('/api/user/' + user.id);
}
validateUser(null)
  .then(data => console.log(data))
  .catch(err => console.error(err)); // 输出:用户信息无效

 

throw vs Promise.reject.

在 async 函数中,throw error 等价于 return Promise.reject(error)。

但在普通函数(非 async)的 .then 回调中:

  • throw error → 会进入 .catch(效果相同)。
  • return Promise.reject(error) → 也会进入 .catch。

两者在 .then 回调中效果几乎一样,但 Promise.reject 更明确地表示“我要返回一个失败的 Promise”,而 throw 是抛出异常。

在你封装的拦截器中,因为回调函数可能是 async 也可能不是,Promise.reject 是一种更安全、更显式的写法。

 

 

Promise.all() —— 并发执行,全部成功才成功

 

const p1 = fetch('/api/user');
const p2 = fetch('/api/posts');
Promise.all([p1, p2])
  .then(([userRes, postsRes]) => {
    // 两个请求都成功后才执行
    return Promise.all([userRes.json(), postsRes.json()]);
  })
  .then(([user, posts]) => {
    console.log('用户', user);
    console.log('文章', posts);
  })
  .catch((err) => {
    // 只要有一个失败,就会进入这里
    console.error('某个请求失败了', err);
  });

 

Promise.race() —— 谁先完成就返回谁

 

const p1 = new Promise(resolve => setTimeout(resolve, 1000, '慢'));
const p2 = new Promise(resolve => setTimeout(resolve, 500, '快'));
Promise.race([p1, p2]).then(value => {
  console.log(value); // 输出 '快'(因为 p2 先完成)
});

 

Promise.allSettled() —— 等待所有都完成(无论成功失败)

const p1 = Promise.resolve('成功');
const p2 = Promise.reject('失败');
Promise.allSettled([p1, p2]).then(results => {
  console.log(results);
  // [
  //   { status: 'fulfilled', value: '成功' },
  //   { status: 'rejected', reason: '失败' }
  // ]
});

 

Promise.any() —— 只要有一个成功就返回,全部失败才 reject

 

const p1 = Promise.reject('错误1');
const p2 = Promise.resolve('成功');
const p3 = Promise.reject('错误2');
Promise.any([p1, p2, p3]).then(value => {
  console.log(value); // '成功'
});

 

7. async/await —— 更甜美的语法糖

虽然本博客是讲 Promise,但实际开发中我们几乎都使用 async/await,它让异步代码看起来更像同步。

async function fetchData() {
  try {
    const user = await getUser();
    const orders = await getOrders(user.id);
    console.log(orders);
  } catch (error) {
    console.error('捕获异常:', error);
  }
}

await 后面跟一个 Promise,会阻塞后面的代码,直到 Promise 完成。async 函数默认返回一个 Promise。

注意:await 只能在 async 函数中使用,而且必须配合 try/catch 来处理错误,否则错误会被“静默”忽略。

 

8. 实际应用小案例

 

场景:并发请求数据,并在所有数据返回后统一渲染

async function initPage() {
  const [user, posts, comments] = await Promise.all([
    fetch('/user').then(res => res.json()),
    fetch('/posts').then(res => res.json()),
    fetch('/comments').then(res => res.json())
  ]);
  renderUser(user);
  renderPosts(posts);
  renderComments(comments);
}
initPage().catch(err => console.error('页面加载失败', err));

 

场景:超时控制(例如请求超过3秒就中断)

function fetchWithTimeout(url, timeout = 3000) {
  const controller = new AbortController(); // 可选,结合 fetch
  const timer = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('请求超时')), timeout)
  );
  const request = fetch(url, { signal: controller.signal });
  return Promise.race([request, timer]);
}
fetchWithTimeout('/api/slow')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error(err.message));

 

9. 核心要点 & 最佳实践

要点 说明
始终返回 Promise 在 .then() 中要么返回普通值(会自动包装),要么返回 Promise,以保持链式
永远不要忘记 .catch() 或者用 try/catch 包裹 await,否则错误会被吞没
Promise 创建后立即执行 new Promise 中的代码是同步执行的,只有 .then 是异步回调
状态不可逆 一旦 resolve/reject,后续再调用无效
优先使用 async/await 可读性更高,错误处理更自然,但底层依然是 Promise

 

 

10. 总结

Promise 让异步编程变得可控、可读、可组合。无论你用 .then().catch() 还是 async/await,理解它的核心思想(状态、链式、错误传递)都能让你写出更健壮的代码。

如果你刚接触,不妨自己动手写几个例子,跑跑看,感觉一下“链式”和“并发”的魅力。如果已经熟悉,希望这篇也能帮你查漏补缺。

 


评论
User Image
提示:请评论与当前内容相关的回复,广告、推广或无关内容将被删除。

相关文章
需求 作为前端开发,异步编程是我们每天都要面对的。而 Promise 就是解决异步问题的“终极武器”。无论你是刚入门,还是已经用过 async/await,搞懂
Promise 的含义Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了
问题描述Uncaught (in promise) TypeError: FileSaver.saveAs is not a function环境说明 已引入js资源文件
​JavaScript 数学小数精确计算<!DOCTYPE html&gtl; <html&gtl; <head&gtl; <title&gtl;</tit...
引言AES代表高级加密系统,它是一种对称加密算法
在vue2.0中使用了axios库,设置请求头Content-Type='application/json;charset=UTF-8'无效axios.defaults.headers.comm...
java json字符串转对象_json转换为java对象_ json字符串转对象数组
1.jQuery ajax$.ajax({ type: 'POST', url: url, data: data, dataType: dataType, ...
主流模块规范目前主流模块规范有:规范名称运行环境实现加载方式AMD(异步模块定义)客户端require.js异步CMD(通用模块定义)客户端sea.js异步Co
有些时候你可能会导出大量的JSON数据到文件中,或者说将所有数据导入到JSON文件。和任何大数据集一样,您不能只将其全部放入内存并将其写入文件。 它需要一段时间
       学习使用Gson JsonReader类,这是一个基于拉的流式JSON解析器,它有助于将JSON作为标记流来读取​GSON1. JsonReader是什么JsonReader是流式...
httpclient4.5使用详解 httpclient 4.5 post传递json参数
Java EE 8 JSON Pointer讲解,Java EE 8包含JSON处理API的更新,并为最新的JSON标准提供最新的IEFT标准。
layer.prompt弹出框设置默认值,layer.prompt弹出框如何默认值,偶尔需要给layer的弹出设置默认值这里详细讲解一下
JavaScript/jquery选择同级兄弟节点选择上下级节点方法<script&gtl;function js_selector() {var s= documen