前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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.resolve thenable
JavaScript异步与 Promise

Promise.resolve 传入一个 thenable 对象时会发生什么?

Promise.resolve 会新建一个 Promise,在下一个微任务里调用 thenable 的 then(resolve, reject) 方法,then 里调了哪个,新 Promise 就变成什么状态。只有传入的是原生 Promise 实例时,才会原样返回。

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

`Promise.resolve` 按参数类型分三种处理

  1. 原生 Promise 实例constructor 就是 Promise,直接原样返回,不包新壳
  2. thenable新建 Promise,下一个微任务调用它的 then,跟随它的结果
  3. 普通值直接返回一个已经 fulfilled 的新 Promise

then 如果从头到尾不调用 resolve 或 reject,返回的 Promise 会一直停在 pending,后面的 .then 永远不会执行,也不会报错。

核心回答

先记住这个答案

Promise.resolve(thenable) 不会把这个对象原样还给你。它会新建一个 Promise,然后在下一个微任务里调用 thenable.then(resolve, reject),把新 Promise 的 resolve 和 reject 交给它。then 里调了 resolve(42),新 Promise 就以 42 兑现;调了 reject,就被拒绝。只有传进来的本身就是原生 Promise 实例时,Promise.resolve 才会直接返回同一个对象。

  • thenable 就是带 then 方法的对象或函数
  • 返回的是新 Promise,结果由 then 里调用的回调决定
  • then 在下一个微任务才被调用,不是同步调用
  • 只有原生 Promise 实例才会被原样返回

先看一个 thenable 被展开的过程

thenable 指的是任何带 then 方法的对象或函数。它不需要是 Promise,只要长得像就行。比如下面这个普通对象:

普通对象也能被当成 Promise 等待JavaScript
const thenable = {
  then(resolve, reject) {
    console.log('then 被调用')
    resolve(42)
  }
}

const p = Promise.resolve(thenable)
console.log('同步代码结束', p === thenable) // p 是新建的 Promise
p.then((v) => console.log('结果', v))
查看输出与解释
同步代码结束 false
then 被调用
结果 42

先打印的是「同步代码结束」,说明 then 不是在 Promise.resolve 调用时同步执行的,而是被放进了微任务队列。等同步代码跑完,引擎才去调用 thenable.then,这时 resolve(42) 让 p 变成 fulfilled,p.then 的回调再进下一个微任务,打印出 42。 可以把 Promise.resolve 想成一个转接头:外面插进来的是什么牌子的插头都行,只要有 then 这个接口,它就能转成标准 Promise。

什么时候会原样返回

原样返回的条件很窄:参数必须是 Promise 实例,而且它的 constructor 正好是调用方的 Promise。子类的实例、自己手写的 Promise 都不算。

原生实例、构造函数写法和子类的对比JavaScript
const native = Promise.resolve(1)
console.log(Promise.resolve(native) === native) // 原样返回
console.log(new Promise((r) => r(native)) === native) // 构造函数总是新建

class MyPromise extends Promise {}
const mine = MyPromise.resolve(1)
console.log(Promise.resolve(mine) === mine) // constructor 是 MyPromise,不是 Promise
查看输出与解释
true
false
false

new Promise((r) => r(native)) 永远得到新对象,而且它会把 native 当 thenable 走一遍上面的流程,比 Promise.resolve(native) 多花两个微任务才兑现。所以把已有的 Promise 统一成 Promise 时,直接用 Promise.resolve 更省。

为什么要兼容 thenable
ES6 之前社区里已经有 Q、Bluebird、jQuery Deferred 等一堆 Promise 实现。Promises/A+ 规范约定大家都认 then 方法,这样不同库的 Promise 能互相串起来。原生 Promise 延续了这个约定,await 一个 thenable 也是同样的处理方式。

then 写得不规范时会怎样

thenable 是外部代码,可能乱调回调。Promise 的做法是只认第一次,后面的调用和异常一律忽略。

多次 resolve 加抛错JavaScript
const bad = {
  then(resolve, reject) {
    resolve('第一次')
    resolve('第二次') // 被忽略
    throw new Error('已经 resolve 过,抛错也被忽略')
  }
}

Promise.resolve(bad).then(
  (v) => console.log('fulfilled', v),
  (e) => console.log('rejected', e.message)
)
查看输出与解释
fulfilled 第一次

反过来,如果 then 在调用任何回调之前就抛错,新 Promise 会以这个错误被拒绝。如果 then 什么都不调,新 Promise 就永远 pending,这是最难排查的一种,因为不会有任何报错。

回答前,多想一步

容易答错的地方

说 Promise.resolve 会直接返回传入的对象
这只对原生 Promise 实例成立。thenable、子类实例、普通值都会得到一个新 Promise。面试里把 thenable 和原生 Promise 混为一谈,面试官基本会接着追问区别。
以为 then 是同步调用的
then 方法被放进微任务里调用,所以 Promise.resolve(thenable) 之后紧跟的同步代码会先执行。但读取 then 这个属性是同步的,如果 then 是个会抛错的 getter,错误会在调用时就被捕获,返回的 Promise 直接变成 rejected。
以为只有对象才算 thenable
函数也是对象,一个挂了 then 方法的函数同样会被当成 thenable。反过来,then 属性不是函数的对象只会被当普通值,直接兑现为这个对象本身。
试着用自己的话回答

面试官还会怎么问?

await 一个 thenable 会发生什么?

和 Promise.resolve 一样。await 内部先把值转成 Promise,遇到 thenable 就调用它的 then,拿到 resolve 的值作为 await 表达式的结果。这也是为什么有些 ORM 的查询构造器没有真的返回 Promise,却可以直接 await。

为什么 resolve 一个 Promise 会多出两个微任务?

在 new Promise 的 executor 里调用 resolve(p) 时,p 被当成 thenable。第一个微任务去调用 p.then,这时 p 已经兑现,它的回调再排一个微任务,才轮到外层 Promise 兑现。这就是 async 函数里 return 一个 Promise 会比 return 普通值晚两拍的原因。

怎么判断一个值是不是 thenable?

规范的判断方式是:值是对象或函数,并且它的 then 属性是函数。写成代码就是 value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function'。不要用 instanceof Promise 判断,那样会漏掉其它库的 Promise。

从一道题,走向一组知识

把知识连起来

异步与 Promise

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

Promise.all 内部会对每一项调用 Promise.resolve,所以数组里放 thenable 也能正常等待。

异步与 Promise

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

executor 是同步执行的,而 thenable 的 then 要等一个微任务,两者正好对照着记。

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

参考资料

  • Promise.resolve()

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

本题目录
  1. 先记住这个答案
  2. 先看一个 thenable 被展开的过程
  3. 什么时候会原样返回
  4. then 写得不规范时会怎样
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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