什么是Promise_JavaScript中如何处理异步操作

Promise 是封装异步任务三种状态(pending/fulfilled/rejected)的状态容器,非语法糖;executor 中必须调用 resolve 或 reject,.then/.catch 返回新 Promise,async/await 是其语法包装。

Promise 不是“用来替代回调函数的高级语法糖”,而是 JavaScript 中为解决异步流程控制而设计的**状态容器**——它本身不执行异步操作,只封装和管理异步任务的**待定(pending)、成功(fulfilled)、失败(rejected)** 三种状态及其流转逻辑。

Promise 构造函数必须传入 executor 函数

创建 Promise 实例时,new Promise() 的参数是一个立即执行的函数,叫 executor,它接收两个预置函数:resolvereject。这两个函数不是你定义的,而是 Promise 内部注入的。

常见错误是忘记调用它们,或在条件分支中漏掉其中一个:

const p = new Promise((resolve, reject) => {
  if (Math.random() > 0.5) {
    resolve('success'); // ✅
  }
  // ❌ 没有 else reject,Promise 将永远卡在 pending
});
  • resolve() 触发 fulfilled 状态,后续 .then() 可捕获值
  • reject() 触发 rejected 状态,后续 .catch().then(null, fn) 可捕获错误
  • executor 内抛出未捕获异常,等价于自动调用 reject()

.the

n() 和 .catch() 的链式调用不是“连续执行”,而是返回新 Promise

每次调用 .then().catch() 都会返回一个新的 Promise 实例,它的状态取决于回调函数的返回值或抛出的错误:

Promise.resolve(1)
  .then(x => x + 1)        // 返回 2 → 新 Promise(fulfilled, 2)
  .then(x => Promise.resolve(x * 2)) // 返回 Promise(fulfilled, 4)
  .then(x => { throw new Error('boom') }) // 抛错 → 新 Promise(rejected, error)
  .catch(err => console.log(err.message)); // 'boom'
  • 回调函数返回普通值(如数字、字符串),新 Promise 状态为 fulfilled,值为该返回值
  • 回调函数返回另一个 Promise,新 Promise 状态和值将**跟随**那个 Promise(即“扁平化”)
  • 回调函数抛出异常,新 Promise 状态为 rejected,错误为该异常
  • .catch() 实际是 .then(undefined, onRejected) 的语法糖,只捕获前一个 Promise 的 rejection

async/await 是 Promise 的语法包装,不是独立异步模型

async 函数本质是返回 Promise 的函数;await 本质是暂停当前 async 函数执行,等待右侧 Promise settle 后继续,并把结果当作普通值处理。

以下三段代码行为完全等价:

function fetchUser() {
  return fetch('/api/user').then(res => res.json());
}
async function fetchUser() {
  const res = await fetch('/api/user');
  return res.json();
}
async function fetchUser() {
  return (await fetch('/api/user')).json();
}
  • await 只能在 async 函数内使用,否则报 SyntaxError: await is only valid in async functions
  • await 右侧如果不是 Promise,会被自动包装成 Promise.resolve(value)
  • 想并发执行多个 Promise,别写 await p1; await p2;(串行),改用 await Promise.all([p1, p2])

真正容易被忽略的是:Promise 的状态一旦改变(resolvereject),就不可逆,也无法被外部干预;所有 .then() 回调都通过微任务队列排队执行,这意味着它们总在当前同步代码之后、下一次事件循环之前运行——这个时机对调试竞态逻辑很关键。