跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何手动实现一个支持“静态状态切换”与“微任务调度”的基础 Promise 构造器

Promise 构造器必须同步执行 executor,但 resolve/reject 不立即触发 then 回调;所有 onFulfilled/onRejected 必须异步且走微任务;then 必须返回新 Promise 并遵循 Promise Resolution Procedure;状态常量应统一定义,实例属性需在 constructor 中明确初始化。 Promise 构造器必须同步执行 executor,但 resolve/reject 不代表立即触发 then 回调 很多人误以为
resolve(value)
一调用,
then
里的回调就立刻执行——这是错的。Promises/A+ 明确要求:所有
onFulfilled
和
onRejected
必须异步调用,且必须走微任务(microtask),不能是宏任务(如
setTimeout
)。 实操要点: 构造函数内必须立即
try { executor(resolve, reject) } catch (e) { reject(e) }
resolve
和
reject
函数内部只负责更新状态(
this.status
)和保存值(
this.value
或
this.reason
),**不直接执行回调** 真正执行注册的回调,要靠
queueMicrotask(() => { /* 遍历并调用 onFulfilledCallbacks */ })
若环境不支持
queueMicrotask
(如 Node.js Promise.resolve().then(fn) 模拟; 绝不能用
setTimeout(fn, 0)
,它会把回调推到下一轮事件循环,违反规范 三态不可逆 + 状态检查必须嵌入 resolve/reject 内部 Promise 的
PENDING → FULFILLED
和
PENDING → REJECTED
是单向的,一旦状态变更,后续任何
resolve
或
reject
调用都应被忽略。这个“忽略”不是靠外部逻辑控制,而是必须在
resolve
/
reject
函数体第一行做判断。 常见错误现象: 没加状态守卫,导致多次
resolve
把
this.value
覆盖,或触发重复回调 把状态判断放在
then
注册时,而不是在
resolve
执行时——这无法阻止 executor 内部多次调用
resolve(1); resolve(2);
正确写法示例片段:
function resolve(value) { if (self.status !== PENDING) return; // 关键守卫 self.status = FULFILLED; self.value = value; queueMicrotask(() => { self.onFulfilledCallbacks.forEach(cb => cb(value)); }); }
then 必须返回新 Promise,且返回值处理依赖 resolvePromise 辅助函数
promise.then(onFulfilled, onRejected)
每次调用都必须返回一个全新 Promise 实例,它的终态由回调函数的返回值
x
决定——这不是简单赋值,而是一套递归展开规则(即 Promises/A+ 的 “Promise Resolution Procedure”)。 容易踩的坑: 直接
return new MyPromise(...)
但没处理
x
类型:比如
x
是另一个
MyPromise
实例、是 thenable 对象、甚至是
undefined
或循环引用(
x === promise2
) 没用
try/catch
包裹
onFulfilled
执行,导致抛错后新 Promise 没被
reject
,错误静默丢失 把
resolvePromise
写成普通工具函数却忘了传入新 Promise 的
resolve
/
reject
引用,造成闭包丢失 关键参数签名必须是:
resolvePromise(promise2, x, resolve, reject)
,其中
promise2
是
then
返回的那个新实例。 静态状态常量定义与实例属性初始化要分离 状态字面量(
'pending'
、
'fulfilled'
)建议用类静态属性或模块级常量,而非硬编码字符串——既防拼写错误,也利于后期扩展(如加
MyPromise.STATUS
命名空间)。 但注意:实例自身的
this.status
、
this.value
、
this.reason
、
this.onFulfilledCallbacks
等,必须在
constructor
中初始化,且初始值要明确:
this.status = MyPromise.PENDING
(不能是
undefined
或空字符串)
this.value = undefined
(即使成功值为
0
或
false
,也要区分“未设置”和“设为 falsy 值”)
this.onFulfilledCallbacks = []
(空数组,不是
null
或
undefined
,否则
push
会报错) 最易被忽略的细节是:
onFulfilledCallbacks
和
onRejectedCallbacks
这两个队列,在
status
变更为非
PENDING
后,必须清空或重置为新数组——否则下次再调用
then
,旧回调可能被重复执行。

相关文章