普通视图

发现新文章,点击刷新页面。
昨天 — 2025年11月29日首页

从「似懂非懂」到「了如指掌」:Promise 与原型链全维度拆解

2025年11月29日 17:36

前言

在前端世界里,Promise原型链(Prototype) 是两个看似毫不相干,却又互相影响、甚至能相互解释的重要概念。

很多人学习 Promise 时,会关注它的使用:thencatchfinallyPromise.all 等;
学习原型链时,又会关注 __proto__prototype、构造函数与实例之间的关系。

但鲜有人把 Promise 本身也是一个对象,它也依赖原型链运作 这件事真正联系起来讲透。

本文将以一次完整的 Promise 异步流程为主线,把“原型链 + 状态机 + 微任务”融合讲解,让你完全理解 Promise 到底是怎么在底层“跑”起来的。


一、Promise 为什么是“对象”?

我们常常写:

const p = new Promise((resolve, reject) => {
  setTimeout(() => resolve('OK'), 1000)
})

很多人知道 Promise 是“异步解决方案”,但忽略了一个基本事实:

Promise 是一个构造函数(类),你创建的是它的实例。

也就是说:

  • Promise —— 构造函数(带 prototype
  • p —— 实例对象(带 __proto__

打开控制台试试:

console.log(p.__proto__ === Promise.prototype) // true

这里马上就把原型链扯进来了。

🔍 Promise.prototype 上都有啥?

输入:

console.log(Promise.prototype)

你会看到:

then: ƒ then()
catch: ƒ catch()
finally: ƒ finally()
constructor: ƒ Promise()
...

这说明:

所有 Promise 实例都是通过原型链访问 then/catch/finally 的。

也就是说 p.then() 并不是实例自身有,而是:

p ---> Promise.prototype ---> Object.prototype ---> null

这为后文理解 Promise “链式调用”机制奠定基础。


二、原型链视角下,看懂 Promise 的执行流

我们直接看一个你提供的代码精简版:

const p = new Promise((resolve, reject) => {
  console.log(111)
  setTimeout(() => {
    reject('失败1')
  }, 1000)
})

console.log(222)

p.then(data => {
  console.log(data)
}).catch(err => {
  console.log(err)
}).finally(() => {
  console.log('finally')
})

输出顺序:

111
222
失败1
finally

要理解为什么 Promise 能这样执行,必须从两个角度讲:

  • (1)Promise 内部是状态机(pending → fulfilled / rejected)
  • (2)then/catch/finally 是通过原型链挂载的“回调注册器”

我们分开看看。


1)Promise 内部是一个状态机

内部状态(无法手动修改):

状态 描述 何时出现
pending 初始状态 执行 executor 期间
fulfilled resolve 被调用 成功
rejected reject 被调用 失败

也就是说:

new Promise(executor)

执行后:

  • 立即执行 executor
  • executor 只在同步阶段运行
  • 真正的 resolve/reject 回调是“挂起来”,等事件循环驱动

所以你看到:

111(executor 同步执行)
222(外部同步执行)
失败1(异步到点后 reject)
finally(状态 settled 后触发)

2)then/catch/finally:它们不是魔法,是原型链的方法

看看这段链式调用:

p.then(...).catch(...).finally(...)

为什么可以一直“链式”?

因为每次调用 then 都 返回一个新的 Promise 实例

p.then(...) → p2
p2.catch(...) → p3
p3.finally(...) → p4

这几个实例的原型链依然是:

p2.__proto__ === Promise.prototype
p3.__proto__ === Promise.prototype
...

因此:

链式本质 = 每次链式都返回一个新的 Promise 实例,然后继续在原型链上查找 then/catch/finally。

这就是原型链在 Promise 底层的重要性。


三、原型链的类比:Promise 就像“火车头 + 车厢”系统

你提到的类比非常棒,我把它整理成完整模型:

✨ Promise = 火车系统

  • 构造函数(Promise) = 火车制造厂
  • 原型对象(Promise.prototype) = “火车车厢模板”
  • 实例(p) = 火车头
  • then/catch/finally = 可以接在车头后的“车厢类型”

于是我们看到:

p(车头).then(挂一个车厢)
         .then(再挂一节)
         .catch(挂一个处理失败的车厢)
         .finally(挂尾部的清理车厢)

每次挂车厢(调用 then/catch)时,都会生成 新的火车车头(新的 Promise 实例)

整个火车最终沿着轨道(事件循环)开动到终点。

⚠️ 注意:为什么 finally 一定执行?

因为 finally 不关心结果,只关心火车是否开到终点(settled)。


四、Promise 与普通对象原型链的对比

你提供了一个经典例子:

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.speci = '人类'

let zhen = new Person('白兰地空瓶', 18)
console.log(zhen.speci)

const kong = {
  name: '空瓶',
  hobbies: ['读书', '喝酒']
}

zhen.__proto__ = kong

console.log(zhen.hobbies, zhen.speci)

输出:

人类
['读书','喝酒'] undefined

这个例子非常适合用来对比 Promise 的原型链逻辑。

对比 1:实例可以动态改原型(不推荐)

zhen.__proto__ = kong 改掉了原来的 Person.prototype

所以:

  • 能访问 hobbies:因为来自 kong
  • 不能访问 speci:因为已脱离 Person.prototype

Promise 则不能做这种事

你不能这样做:

p.__proto__ = {}

否则:

p.then is not a function

因为 then/catch/finally 都来自 Promise.prototype。

这反而让我们更清楚地理解:

Promise 的能力几乎全部来自原型链。


五、Promise.all 的底层逻辑:一辆多车头的“联挂火车”

提到 Promise.all,这里正好顺便讲讲它的底层设计。

Promise.all([p1, p2, p3])

机制可以用一个形象类比解释:

  • 假设有三辆火车(p1/p2/p3)
  • Promise.all 创建一辆“总火车头” pAll
  • pAll 盯着三个火车头,只要全部变成 fulfilled,就把所有结果一次性返回
  • 如果有一个 reject,则整个 pAll 变成 rejected(列车脱轨)

也就是说:

Promise.all = 多个 Promise 状态机的并联 + 一个新的总状态机。

为什么它能做到?

答案依旧在原型链:

  • Promise.all 本质是一个静态方法,返回新的 Promise 实例
  • 新的 Promise 实例依然沿用同一套路(prototype → then/catch)

六、用真实工程场景收尾:Promise 原型链为什么重要?

在真实项目里,理解 Promise 的原型机制有三个实际价值:

① debugger 时能看清原型链,定位异步回调来源

你能区分:

  • then 回调从哪里来的?(Promise.prototype.then)
  • promise 链断在哪一层?

② 手写 Promise 时必须实现 then/catch/finally

如果你手写 Promise A+:

MyPromise.prototype.then = function(onFulfilled, onRejected) {}

这里你就必须自己处理链式、状态机、回调队列。

③ 能理解 async/await 的底层依赖 Promise 链式调度

await 会把后续步骤注册到 promise.then 中。

理解 then 的原型链,就能理解 async/await 的机制本质。


七、总结:Promise + 原型链的全景图

// 创建实例
const p = new Promise(executor)

// 原型链:调用能力来自这里
p.__proto__ = Promise.prototype

// 状态机:内部维护 pending → fulfilled/rejected

// then/catch/finally:注册微任务

// 链式调用:每次都返回一个新的 Promise 实例

// Promise.all:多个状态机的并联

一句话总结:

Promise 本质是一个基于“原型链 + 状态机 + 微任务队列”的异步调度框架。

它既是面向对象设计(通过原型链复用方法),又是异步控制核心工具(内部状态机)。

理解二者的融合,你就真正吃透了 Promise。

❌
❌