跳到主要内容

手写(复制粘贴)Promise

手写一个符合 Promise/A+ 规范的 Promise ,需要理解其核心机制:状态管理、异步回调、链式调用、错误传递。

以下代码完全自(ctrl)主(c)研(腾讯)发(元宝)

全码


/\*\*

- 辅助函数:处理 Promise 解析逻辑(Promise/A+ 规范核心)
-
- @params promise2
- @params returnValue 返回值
- @params resolve 完成方法
- @params reject 拒绝方法
- \*/
function resolvePromise(promise2, returnValue, resolve, reject) {
// 1. 防止循环引用(如 promise2.then 返回 promise2 自己)
if (promise2 === returnValue) {
return reject(new TypeError('检测到 Promise 的循环链'));
}

// 2. 如果 returnValue 是 MyPromise 实例,递归解析
if (returnValue instanceof MyPromise) {
returnValue.then(
_result => resolvePromise(promise2, _result, resolve, reject),
err => reject(err)
);
return;
}

// 3. 如果 returnValue 是对象/函数,尝试获取 then 方法(处理 thenable 对象)
let then;

try {
then = returnValue?.then;
} catch (err) {
return reject(err); // 获取 then 失败,直接 reject
}

// 4. 如果 then 是函数,当作 “thenable” 对象处理
if (typeof then === 'function') {
// 防止多次调用 resolve/reject
let called = false;

try {
then.call(
returnValue,
_result => {
if (called) return;
called = true;
resolvePromise(promise2, _result, resolve, reject); // 递归解析 _result
},
err => {
if (called) return;
called = true;
reject(err); // 调用 onRejected,传递错误
}
);
} catch (err) {
if (called) return;
reject(err); // 调用 then 失败,reject
}

} else {
// 5. 如果 returnValue 是普通值(非对象/函数/thenable),直接 resolve
resolve(returnValue);
}
}

// 自定义 Promise 类
class MyPromise {
// 状态:pending/fulfilled/rejected(不可逆)
state = 'pending';
// 成功值
value = undefined;
// 失败原因
reason = undefined;
// 存储 pending 状态下的回调(解决同步 resolve 时 then 未注册的问题)
onFulfilledCallbacks = [];
onRejectedCallbacks = [];

// 初始化
constructor(executor) {
// executor 是用户传入的函数,接收 resolve/reject
const resolve = (value) => {
// 状态已改变,跳过
if (this.state !== 'pending') return;

this.state = 'fulfilled';
this.value = value;
// 执行所有 pending 状态下的 fulfilled 回调(异步)
this.onFulfilledCallbacks.forEach(cb => cb());
};

const reject = (reason) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb());
};

try {
executor(resolve, reject); // 执行用户逻辑
} catch (err) {
reject(err); // executor出错,直接reject
}

}

// then 方法:注册 fulfilled/rejected 回调,返回新 Promise(链式调用)
then(onFulfilled, onRejected) {
// 返回新 Promise,确保链式调用
const promise2 = new MyPromise((resolve, reject) => {
// 处理 fulfilled 状态的回调
const handleFulfilled = () => {
queueMicrotask(() => { // 异步执行(模拟微任务)
try {
if (typeof onFulfilled === 'function') {
// 调用用户回调
const returnValue = onFulfilled(this.value);
// 处理返回值
resolvePromise(promise2, returnValue, resolve, reject);
} else {
resolve(this.value); // 未传 onFulfilled,直接传递值
}
} catch (err) {
reject(err); // 回调出错,reject 新 Promise
}
});
};

// 处理rejected状态的回调
const handleRejected = () => {
queueMicrotask(() => {
try {
if (typeof onRejected === 'function') {
// 调用用户回调
const returnValue = onRejected(this.reason);
resolvePromise(promise2, returnValue, resolve, reject);
} else {
// 未传onRejected,直接传递错误
reject(this.reason);
}
} catch (err) {
reject(err);
}
});
};

// 根据当前状态执行回调
if (this.state === 'fulfilled') {
handleFulfilled();
} else if (this.state === 'rejected') {
handleRejected();
} else {
// pending状态,存储回调等待resolve/reject
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});

return promise2;

}

// catch 方法:语法糖,仅处理 rejected
catch(onRejected) {
return this.then(null, onRejected);
}

// 静态方法:创建已 resolve 的 Promise
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}

// 静态方法:创建已 reject 的 Promise
static reject(reason) {
return new MyPromise((\_, reject) => reject(reason));
}
}

状态管理

Promise 有三种状态:

  • pending:初始状态,等待异步操作完成
  • fulfilled:异步操作成功,储存结果 value
  • rejected:异步操作失败,储存原因 reason

状态改变仅能从 pendingfulfilledpendingrejected,且不可回退

异步处理

  • 储存回调:当 promise 处于 pending 状态时, then 注册的回调会被储存入 onFulfilledCallbacks/onRejectedCallbacks 数组,等待 resolve/rejected 触发
  • 异步执行:使用 queueMicrotask (模拟微任务) 确保 then 的回调异步执行,符合 Promise/A+ 规范

链式调用

then 方法返回新的 Promise,实现链式调用。新的 Promise 的状态由 then 回调的返回值决定:

  • 如果回调返回普通值promise2 直接 resolve 该值
  • 如果回调返回 Promisepromise2 的状态由返回的 Promise 决定(递归解析)
  • 如果回调抛出错误promise2 直接 reject 该错误

错误的调用

  • Executor 错误:执行用户传入的 executor 时出错,直接 reject
  • Then 回调错误then 注册的回调错误,错误会传递给下一个 thenonRejectedcatch

resolvePromise 函数

处理 then 回调返回值 returnValue 的所有可能情况:

  • 循环引用检测:防止 promise2.then 返回 promise2 自身,导致无限递归
  • Promise 实例:递归解析返回的 Promise,直到其状态改变
  • Thenable 对象:尝试调用 then 方法,递归解析其结果(兼容原生 Promisethenable
  • 普通值:直接 resolve 该值

注意事项

  • 异步性then 的回调始终异步执行,即使 resolve 是同步调用的
  • 状态不可逆:一旦状态更改,无法回退
  • 错误捕获:所有的错误(executorthen 回调)都需要通过 reject 传递,最终由 catch 捕获
  • 链式调用then 返回新的 Promise,确保后续的回调能处理前序结果
  • 循环引用resolvePromise 中需检测 promise2 === returnValue ,避免无限递归

执行解析(我没看懂源码,继续追问的元宝)

使用一段实例代码追问的解析:

new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log('决议前');
resolve(true); // 触发Promise状态变更
console.log('决议后');
}, 100);
}).then(res => {
console.log(res); // 为何在“决议后”之后执行?
});

初始化 Promise,注册 then 回调

执行 new MyPromise(executor) , 传入的 executor 立即执行:

  • 遇到 setTimeout ,将其回调(宏任务)放入宏任务列队,等待执行
  • then 方法被调用,返回新的 Promise,但当前的 Promise 状态还是 pending ,因此 then 的两个回调 (onFulfilledonRejected)被暂存onFulfilledCallbacks 数组中

宏任务(setTimeout 回调)执行

100 ms 后,setTimeout 的回调执行触发(宏任务开始执行)

  • 打印 "决议前"
  • 调用 resolve(true)
    • Promise 状态从 pending 变化为 fulfilled
    • 执行 onFulfilledCallbacks 数组中的回调(即 handleFulfilled 函数)
  • 打印 "决议后"

handleFulfilled 触发微任务调度

handleFulfilled 是手写 Promise 中定义的回调函数,内部使用 queueMicrotask 包裹了真正的处理逻辑

const handleFulfilled = () => {
queueMicrotask(() => {
// 关键:将逻辑包装成微任务
try {
if (typeof onFulfilled === 'function') {
const returnValue = onFulfilled(this.value); // 调用 then 的回调(打印 res)
resolvePromise(promise2, returnValue, resolve, reject);
} else {
resolve(this.value);
}
} catch (err) {
reject(err);
}
});
};

resolve(true) 被调用时, handleFulfilled 被同步执行,但它的内部的 queueMicrotask 会将真正的 then 回调逻辑放入微任务列队

宏任务结束,执行微任务

setTimeout 的宏任务执行完毕,事件循环开始清空微任务列队

  • 执行 queueMicrotask 中的回调,即调用 onFulfilled(res)res 值为 true
  • 打印 true