一、该自定义 Promise 可复用的核心功能
这个实现已经覆盖了原生 Promise 的核心特性,在简单场景下可以正常使用,包括:
-
- 实现了 Promise 三大状态(
pending/fulfilled/rejected)的切换,且保证状态不可逆转(从pending只能转为fulfilled或rejected,无法回退或重复切换);
- 实现了 Promise 三大状态(
-
- 实现了
then方法的链式调用(返回新的 Promise 实例,避免修改原实例状态),支持成功/失败回调的指定;
- 实现了
-
- 实现了「值穿透/错误穿透」特性,兼容
then方法不传回调的场景;
- 实现了「值穿透/错误穿透」特性,兼容
-
- 实现了常用原型方法
catch(语法糖,基于then实现)和静态方法(resolve/reject/all/race);
- 实现了常用原型方法
-
- 实现了异步回调执行(用
setTimeout模拟)和异常捕获(执行器、回调函数中的异常均可捕获并转为rejected状态);
- 实现了异步回调执行(用
-
- 支持回调函数返回普通值或 Promise 实例,正确透传后续链式调用的结果。
-
- 避免出现: 检测循环引用 和 检测循环依赖 问题。
-
- 对
this指向的更严谨绑定,无论以何种方式调用(如 call/apply/bind 改变 this),都能保证回调的正确执行;
- 对
二、无法替代原生 Promise 的关键差异与局限性
这个自定义实现是「简化版」,与原生 Promise 存在诸多核心差异,这些差异导致它无法在生产环境中替代原生 Promise,具体如下:
1. 缺少原生 Promise 的部分核心 API
该自定义实现未覆盖原生 Promise 的诸多实用 API,无法满足复杂场景需求:
- 原型方法:
finally()(无论 Promise 状态成功/失败,都会执行的回调,用于清理资源); - 静态方法:
allSettled()(等待所有 Promise 完成,无论成功/失败,返回所有结果)、any()(等待第一个成功的 Promise,所有都失败才返回失败); - 其他特性:
Promise.prototype.then的回调参数可选性的更严谨处理、原生的Symbol.species支持等。
2. 回调队列处理的局限性(多 then 绑定场景)
原生 Promise 支持对同一个 Promise 实例多次调用 then 方法,所有回调都会按绑定顺序执行,且处理逻辑更严谨;
该自定义实现的 callbacks 数组虽能存储多个回调,但在极端场景下(如快速多次绑定 then、状态切换与回调绑定并发),可能存在回调执行顺序错乱或重复执行的风险,不如原生 Promise 健壮。
3. 边界场景与异常处理的健壮性不足
原生 Promise 对各种边界场景做了极致兼容,而自定义实现存在诸多遗漏:
- 不支持
resolve/reject传入Promise实例的深层透传(部分场景处理不严谨); - 对
then方法传入的非函数参数的处理不如原生严谨; - 不支持捕获异步执行器中的异常(当前仅能捕获同步执行器的异常,原生 Promise 可捕获异步异常并转为
rejected);
4. 原生 Promise 的性能与优化优势
原生 Promise 是浏览器/Node.js 底层内置实现(多为 C++ 编写),执行效率远高于自定义的 JavaScript 实现,尤其在大量 Promise 实例并发、复杂链式调用场景下,性能差距会非常明显;
同时,原生 Promise 会被 JavaScript 引擎做各种优化(如微任务队列的高效调度),自定义实现无法享受这些优化红利。
示例:
let p = new Promise((resolve) => setTimeout(() => resolve('ok'), 1000));
// 同一个 Promise 实例绑定 3 个 then 回调
p.then(v => console.log('回调1', v));
p.then(v => console.log('回调2', v));
p.then(v => console.log('回调3', v));
原生 Promise 会按顺序输出 3 个回调结果,自定义实现虽大概率能正常执行,但底层处理逻辑未做足够的边界防护。
myPromise 代码如下:
myPromise.html:
<html>
<head><title>test promise</title></head>
<body>
<script type="text/javascript" src="myPromise.js"></script>
<script type="text/javascript">
/*
* 测试代码注释:批量测试 Promise.all 方法
let p1 = new myPromise((resolve, reject) =>{
setTimeout(()=>{
resolve('ok')
},1000)
})
let p2 = myPromise.resolve('hello')
let p3 = myPromise.resolve('good')
let r = myPromise.all([p1,p2,p3])
console.log('r = ',r)
*/
// 测试代码:链式调用 myPromise 的 then 方法,验证自定义 Promise 功能
let p = new myPromise((resolve, reject) =>{
// 延迟 1 秒后,将 myPromise 状态转为成功态,结果为 'ok'
setTimeout(()=>{
resolve('ok')
},1000)
})
// 链式调用 then 方法,验证值穿透、链式调用和状态传递
const res = p.then(value=>{
// throw 'FAILED' // 手动抛出异常,可测试 catch 捕获功能
console.log(value) // 第一个 then:打印成功结果 'ok'
})
.then(value=>{
console.log(111) // 第二个 then:值穿透,打印 111
})
.then(value=>{
console.log(222) // 第三个 then:值穿透,打印 222
return new myPromise(()=>{}) // 返回一个 pending 状态的 myPromise,中断后续链式调用
})
.then(value=>{
console.log('end') // 第四个 then:由于上一个 Promise 是 pending 状态,此回调不会执行
})
.catch(reason=>console.warn(reason)) // 捕获整个链式调用中的所有异常
// 打印最终返回的 myPromise 实例(状态为 pending)
console.log(res)
</script>
</body>
</html>
myPromise.js:
**// defer a myPromise function.
/**
* 自定义 myPromise 构造函数
* @param {Function} executor - 执行器函数,接收 resolve 和 reject 两个回调参数,同步执行
*/
function myPromise(executor){
// 保存当前 myPromise 实例的 this 指向,避免在嵌套函数中丢失
const self = this
// 记录 myPromise 的状态,初始为 pending(等待态)
// 状态只能从 pending -> fulfilled(成功态) 或 pending -> rejected(失败态),不可逆转
self.PromiseState = 'pending'
// 记录 myPromise 的结果值(成功结果或失败原因)
self.PromiseResult = null
// 存储回调函数队列(当 myPromise 状态未改变时,保存 then 方法传入的 onResolved/onRejected 回调)
self.callbacks = []
// 状态守卫:如果状态不是 pending,直接返回(确保状态不可逆转)
if(self.PromiseState!=='pending'){ return null }
/**
* myPromise 成功回调函数
* @param {*} data - 成功的结果数据
*/
function resolve(data){
// 再次守卫:只有 pending 状态才能转为 fulfilled,防止多次调用 resolve
if(self.PromiseState !== 'pending') return
// 检测循环引用:如果传入的是当前 myPromise 实例,抛出 TypeError(模拟原生 Promise 行为). 避免出现如 new myPromise((resolve) => resolve(p)) 这种 "循环 myPromise 引用" 问题
if(data === self){
// 改为调用 reject,抛出循环依赖错误
reject(new TypeError('检测到Promise链式循环'))
return
}
// 1. 修改 myPromise 状态为成功态(fulfilled)
self.PromiseState = 'fulfilled'
// 2. 存储成功的结果数据
self.PromiseResult = data
// 3. 如果存在待执行的回调函数,异步执行 onResolved 回调
// (模拟原生 Promise 的微任务,这里用 setTimeout 模拟为宏任务,简化实现)
if(self.callbacks && self.callbacks.length>0) {
// 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
queueMicrotask(()=>{
self.callbacks.forEach(item=>{
item.onResolved(data)
})
})
}
}
/**
* myPromise 失败回调函数
* @param {*} data - 失败的原因数据
*/
function reject(data){
// 再次守卫:只有 pending 状态才能转为 rejected,防止多次调用 reject
if(self.PromiseState !== 'pending') return
// 检测循环引用:如果传入的是当前 myPromise 实例,抛出 TypeError(模拟原生 Promise 行为). 避免出现如 new myPromise((resolve) => resolve(p)) 这种 "循环 myPromise 引用" 问题
if(data === self){
// 改为调用 reject,抛出循环依赖错误
reject(new TypeError('检测到Promise链式循环'))
return
}
// 1. 修改 myPromise 状态为失败态(rejected)
self.PromiseState = 'rejected'
// 2. 存储失败的原因数据
self.PromiseResult = data
// 3. 如果存在待执行的回调函数,异步执行 onRejected 回调
if(self.callbacks && self.callbacks.length>0) {
// 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
queueMicrotask(()=>{
self.callbacks.forEach(item=>{
item.onRejected(data)
})
}, 0)
}
}
try{
// 同步执行执行器函数,传入 resolve 和 reject 回调
// 若执行器内部抛出异常,直接捕获并调用 reject 转为失败态
executor(resolve, reject)
}catch(e){
reject(e)
}
// 没有 return,所以 new myPromise() 返回 this 对象
}
/**
* myPromise 原型方法 then - 用于指定成功/失败的回调函数,返回一个新的 myPromise 实例(实现链式调用)
* then 方法主要做两件事:
* - 注册回调:将 onResolved 和 onRejected 回调函数保存到 self.callbacks 数组中
* - 检查当前状态:
* - 如果状态已经是 'fullfilled',立即在 microtask 中执行 handle(onResolved)
* - 如果状态是 'rejected',立即执行 handle(onRejected)
* - 如果状态还是 'pending',只保存回调,不执行
*
* @param {Function} onResolved - 成功态的回调函数
* @param {Function} onRejected - 失败态的回调函数
* @returns {myPromise} 新的 myPromise 实例
* 注:
* 1. 原生的 then/catch 回调是微任务
* 2. then 方法被调用时,会立即创建一个新的 myPromise 实例
*/
myPromise.prototype.then = function(onResolved, onRejected){
// 保存当前 myPromise 实例的 this 指向
const self = this
// typeof undefined === 'undefined' → 进入默认处理
// typeof null === 'object' → 进入默认处理
// typeof function(){} === 'function' → 不进入默认处理
// 实现值的穿透(默认回调处理):
// 1. 若 onResolved 不是函数,设置默认成功回调,直接返回接收的结果(实现值穿透)
if(typeof onResolved!=='function' ) { onResolved = value=>{ return value} }
// 2. 若 onRejected 不是函数,设置默认失败回调,直接抛出接收的错误(实现错误穿透)
if(typeof onRejected!=='function' ) { onRejected = reason=>{ throw(reason) } }
// 返回一个新的 myPromise 实例,实现 then 方法的链式调用
return new myPromise((resolve, reject)=>{
/**
* 统一处理成功/失败回调的逻辑封装
* @param {Function} callback - 传入的 onResolved 或 onRejected 回调函数
*/
function handle(callback){
try{
// 执行回调函数,传入当前 myPromise 的结果值,获取回调返回结果
let r = callback(self.PromiseResult)
// 检测循环依赖: 如果回调返回当前 myPromise 实例,抛出 TypeError. 避免出现如 p.then(() => p) 这种 "循环 myPromise 依赖" 问题
if(r === self){
throw new TypeError('检测到Promise链式循环')
}
// 判断回调返回结果是否为 myPromise 实例
if(r instanceof myPromise){
// 若返回 Promise,监听其状态变化,将结果透传给新 myPromise 的 resolve/reject
r.then(v=>resolve(v), r=>reject(r))
}else{
// 若返回非 Promise,将结果作为新 myPromise 的成功值
// 回调返回值处理:当回调函数返回 undefined、null、NaN、0、空字符串 '' 等特殊值时,原生 Promise 会将其作为有效值传递给下一个 then。
resolve(r)
}
}catch(e){
// 若回调执行过程中抛出异常,将异常作为新 myPromise 的失败原因
reject(e)
}
}
// 情况1:当前 myPromise 状态为成功态(fulfilled)
if(this.PromiseState === 'fulfilled') {
// 异步执行回调(模拟原生 Promise 微任务)
// 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
queueMicrotask(()=>{
handle(onResolved)
})
}
// 情况2:当前 myPromise 状态为失败态(rejected)
if(this.PromiseState==='rejected'){
// 异步执行回调(模拟原生 Promise 微任务)
// 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
queueMicrotask(()=>{
handle(onRejected)
})
}
// 情况3:当前 myPromise 状态为等待态(pending)
// 此时回调无法立即执行,存入回调队列,等待状态改变后执行
if(this.PromiseState==='pending'){
this.callbacks.push({
// 封装 onResolved 回调,执行时调用统一处理函数 handle
onResolved: () => { handle(onResolved) },
// 封装 onRejected 回调,执行时调用统一处理函数 handle
onRejected: () => { handle(onRejected) }
})
}
})
}
/**
* myPromise 原型方法 catch - 专门用于指定失败态的回调函数(语法糖,基于 then 方法实现)
* @param {Function} onRejected - 失败态的回调函数
* @returns {myPromise} 新的 myPromise 实例
* 注: 原生的 then/catch 回调是微任务
*/
myPromise.prototype.catch = function(onRejected){
/*当前实现中 then 方法直接使用 const self = this 保存 this 指向,当使用 myPromise.prototype.then.call(null, ...) 等方式改变 this 时,会导致:
1. this 变为 null 或其他非 myPromise 对象
访问 this.PromiseState 或 self.PromiseResult 时出现 TypeError: Cannot read property 'PromiseState' of null
2. 行为与原生 Promise 不一致 */
// 检查 this 是否为 myPromise 实例,确保方法只能在 myPromise 实例上调用(模拟原生 Promise 行为)
if(!(this instanceof myPromise)){
throw new TypeError('myPromise.prototype.catch 调用在不兼容的接收器上')
}
// 调用 then 方法,第一个参数传 null,第二个参数传入失败回调,实现错误捕获
return this.then(null, onRejected)
}
/**
* myPromise 静态方法 resolve - 快速创建一个成功态的 myPromise 实例
* @param {*} value - 成功的结果值(可以是普通值或 myPromise 实例)
* @returns {myPromise} 新的 myPromise 实例
*/
myPromise.resolve = function(value){
return new myPromise((resolve, reject)=>{
// 若传入的值是 myPromise 实例,监听其状态变化,透传结果
if(value instanceof myPromise){
value.then(v=>resolve(v), r=>reject(r))
}else{
// 若传入普通值,直接创建成功态 myPromise
resolve(value)
}
})
}
/**
* myPromise 静态方法 reject - 快速创建一个失败态的 myPromise 实例
* @param {*} value - 失败的原因值
* @returns {myPromise} 新的 myPromise 实例
*/
myPromise.reject = function(value){
return new myPromise((resolve, reject)=>{
// 直接创建失败态 Promise,无论传入的值是否为 Promise,都直接作为失败原因
reject(value)
})
}
/**
* myPromise 静态方法 all - 批量处理多个 myPromise 实例,全部成功才返回成功,一个失败则直接返回失败
* @param {Array} promises - myPromise 实例数组
* @returns {myPromise} 新的 myPromise 实例
*/
myPromise.all = function(promises){
return new myPromise((resolve, reject)=>{
// 记录已成功的 myPromise 数量
let count = 0
// 获取传入的 myPromise 数组长度(处理空数组情况,默认赋值 -1)
let promisesLength = promises.length || -1
// 存储所有 myPromise 的成功结果,保持与传入数组的顺序一致
let arrResults = new Array(promisesLength)
// 遍历所有 myPromise 实例
for(let i=0; i<promisesLength; i++){
// 注意:这里用 promises[i] 而不是 this,因为是静态方法
promises[i].then(v=>{
// 存储当前 myPromise 的成功结果,对应数组索引位置
arrResults[i] = v
// 成功数量加 1
count++
// 当成功数量等于数组长度时,说明所有 myPromise 都成功,返回结果数组
if(count===promisesLength){
resolve(arrResults)
}
},r=>{
// 只要有一个 myPromise 失败,直接返回失败原因,终止后续处理
reject(r)
})
}
})
}
/**
* myPromise 静态方法 race - 批量处理多个 myPromise 实例,返回第一个改变状态的 myPromise 结果(无论成功/失败)
* @param {Array} promises - myPromise 实例数组
* @returns {myPromise} 新的 myPromise 实例
*/
myPromise.race = function(promises){
return new myPromise((resolve, reject)=>{
// 遍历所有 myPromise 实例
for(let i=0,iLen=promises.length; i<iLen; i++){
// 监听每个 myPromise 的状态变化,只要有一个状态改变,立即透传其结果
promises[i].then(v=>{
resolve(v)
},r=>{
reject(r)
})
}
})
}
/**
* 自定义 myPromise 静态方法 resolveDelay - 延迟指定时间后创建一个成功态的 myPromise 实例
* @param {*} value - 成功的结果值(可以是普通值或 myPromise 实例)
* @param {Number} time - 延迟时间(毫秒)
* @returns {myPromise} 新的 myPromise 实例
*/
myPromise.resolveDelay = function(value, time){
return new myPromise((resolve, reject)=>{
// 注意:原代码笔误 setTimeount → 修正为 setTimeout
setTimeout(()=>{
// 若传入的值是 myPromise 实例,监听其状态变化,透传结果
if(value instanceof myPromise){
value.then(resolve, reject)
}else{
// 若传入普通值,延迟后创建成功态 myPromise
resolve(value)
}
}, time)
})
}
/**
* 自定义 myPromise 静态方法 rejectDelay - 延迟指定时间后创建一个失败态的 myPromise 实例
* @param {*} reason - 失败的原因值
* @param {Number} time - 延迟时间(毫秒)
* @returns {myPromise} 新的 myPromise 实例
*/
myPromise.rejectDelay = function(reason, time){
return new myPromise((resolve, reject)=>{
// 注意:原代码笔误 setTimeount → 修正为 setTimeout
setTimeout(()=>{
// 延迟后创建失败态 myPromise
reject(reason)
},time)
})
}

1752

被折叠的 条评论
为什么被折叠?



