前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Promise 状态不可变
JavaScript异步与 Promise

Promise 的状态只能改变一次,多次调用 resolve 或 reject 会怎样?

只有第一次调用有效。resolve 之后再 reject,或者连续 resolve 多次,后面的调用都会被静默忽略,不报错也不改结果。但调用 resolve 不会结束 executor,后面的代码还会继续跑。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#异步与 Promise#异步编程
先看核心答案读代码示例
理解线索

Promise 的三个状态

  1. pending初始状态,唯一可以改变的状态
  2. fulfilled调用 resolve(值) 后进入,结果值被永久保存
  3. rejected调用 reject(原因) 或 executor 抛错后进入,原因被永久保存

resolve 一个还没兑现的 Promise 时,外层状态仍是 pending,但已经「锁定」跟随它了,这时再调 reject 同样无效。

核心回答

先记住这个答案

Promise 的状态只能从 pending 变成 fulfilled 或 rejected,而且只能变一次。第一次调用 resolve 或 reject 决定结果,之后不管再调用多少次、调用哪个,都会被静默忽略,不会抛错,也不会改变已有的值。比如先 resolve('ok') 再 reject(err),Promise 仍然是 fulfilled,值是 'ok'。要注意的是 resolve 只是改状态,不会让 executor 停下来,后面的代码会照常执行。

  • 状态只能从 pending 变一次,变了就定死
  • 后续的 resolve、reject 和抛错都被静默忽略
  • resolve 不会中断代码,想停要写 return
  • resolve 一个 Promise 后就锁定了,再 reject 也无效

在 executor 里连续调用会发生什么

最直接的办法是把几种调用写在一起跑一遍。

先 resolve,再 reject,再 resolveJavaScript
const p = new Promise((resolve, reject) => {
  resolve('第一次')
  reject(new Error('被忽略'))
  resolve('第二次') // 同样被忽略
  console.log('executor 继续执行')
})

p.then(
  (v) => console.log('fulfilled', v),
  (e) => console.log('rejected', e.message)
)
查看输出与解释
executor 继续执行
fulfilled 第一次

两个现象要分开记。第一,只有「第一次」生效,后面的 reject 和 resolve 没有任何效果。第二,console.log 照样打印了,说明 resolve 不是 return。如果 resolve 之后还有请求、写缓存之类的逻辑,它们都会执行。习惯写法是 return resolve(value),一次把状态和流程都收住。 resolve 之后再 throw 也一样会被忽略。executor 抛错本质上等于调用 reject,状态已经定了,这个错误就被吞掉了,控制台里也看不到。

resolve 一个 Promise 时的「锁定」状态

resolve 的参数如果是另一个 Promise,外层不会立刻兑现,而是跟着那个 Promise 走。这时外层的状态还是 pending,但已经没法再改了。

外层 pending,但已经不能 rejectJavaScript
const slow = new Promise((r) => setTimeout(() => r('slow'), 100))

const p = new Promise((resolve, reject) => {
  resolve(slow) // p 锁定跟随 slow,此时仍是 pending
  reject(new Error('来晚了')) // 被忽略
})

p.then(
  (v) => console.log('fulfilled', v),
  (e) => console.log('rejected', e.message)
)
查看输出与解释
fulfilled slow

规范里把这种情况叫 resolved:已经决定了跟谁,但还没有最终结果。所以「pending 状态下还能改」这句话不准确,准确说法是「还没调用过 resolve 或 reject 时才能改」。

为什么这样设计
一个 Promise 可能被很多地方 then,也可能在兑现很久之后才有人 then。如果状态还能变,先拿到结果的和后拿到结果的就会不一致。状态定死之后,Promise 就像一张签好的收据,谁来看、什么时候来看,内容都一样,每个 then 回调也最多只会被调用一次。
回答前,多想一步

容易答错的地方

说多次调用会报错
不会报错,就是静默忽略。这也是它难排查的地方:代码里写了 reject,调试时发现根本没进 catch,往往就是前面已经 resolve 过了。
说 resolve 之后代码就不执行了
resolve 只是一个普通的函数调用,改完状态就返回了。后面的代码是否执行只取决于你有没有 return。
以为可以把 Promise 重置回 pending
Promise 没有提供任何重置或取消的方法。需要「重来一次」就新建一个 Promise,需要取消就用 AbortController 之类的机制在外面控制。
试着用自己的话回答

面试官还会怎么问?

这个特性在实际开发里有什么用?

写超时控制时很常用。把请求和定时器放进同一个 Promise:请求回来就 resolve,定时器到点就 reject,谁先调用谁生效,另一个自动作废,不用额外写标志位去判断「是不是已经超时了」。

手写 Promise 时怎么保证状态只改一次?

在 resolve 和 reject 函数开头判断状态,只有 this.state === 'pending' 时才修改状态、保存值并执行回调,否则直接 return。要支持上面的锁定行为,还得再加一个「是否已 resolve 过」的标志,因为跟随另一个 Promise 期间状态仍是 pending。

状态定了之后再调用 then 还能拿到结果吗?

能。结果被保存在 Promise 内部,任何时候调用 then 都能拿到同一个值,只是回调仍然会放进微任务队列异步执行,而不是立刻同步执行。

从一道题,走向一组知识

把知识连起来

异步与 Promise

new Promise 里传的函数是同步执行还是异步执行?

executor 是同步执行的,所以 resolve 之后的同步代码会立刻跑完,两题连起来看更清楚。

异步与 Promise

Promise.all 传一个空数组进去,会一直 pending 吗?

Promise.all 第一个拒绝就定下结果、后面的拒绝被忽略,靠的正是状态只能改一次。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Promise

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 在 executor 里连续调用会发生什么
  3. resolve 一个 Promise 时的「锁定」状态
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑