Promise的实现
基础前置知识
- Promise是一种用于处理异步操作的技术
- Promise有待定(pending)、已完成(fulfilled)和已拒绝(rejected)三种状态
- then方法可以接受完成或拒绝的数据
- 可以链式调用
跟着感觉走
简单示例
const p1 = new Promise((resolve, reject)=>{
if(true) setTimeout(resolve, 1000, 10)
else setTimeout(reject, 1000, '失败了')
})
p1.then(value=>{console.log(value)}, reason=>{console.log(reason)})
示例分析
- 首先定义一个MyPromise类 初始状态是pending
- 接受一个
executor执行函数,executor会立即执行。然后接受两个参数(resolve,reject)并由MyPromise提供用于改变MyPromise的状态。resolve执行状态变更为fulfilled,reject方法执行状态变更为rejected - 然后调用then方法并提供两个参数,分别对应resolve和reject的回调数据
实现
第一版:同步版本
-
代码
// 首先定义三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" // 定义MyPromise类 class MyPromise { // 定义constructor 并接受一个executor参数 constructor(executor){ // 初始状态 this.status = PENDING // 完成状态下的数据 this.value = undefined // 拒绝状态下的数据 this.reason = undefined // 定义resolve,reject方法用于接受对应数据和改变状态 const resolve = value=>{ this.value = value this.status = FULFILLED } const reject = reason=>{ this.reason = reason this.status = REJECTED } // 接下来执行executor 执行异常直接抛出错误 try { executor(resolve, reject) } catch (error) { reject(error) } } // 定义then方法用于捕获结果并接受两个参数onResolve, onReject分别对应resolve和reject的数据回调 then(onResolve, onReject){ if(this.status === FULFILLED) { onResolve(this.value) } if(this.status === REJECTED) { onReject(this.reason) } } } -
测试
new MyPromise((reslove, reject)=>{ reslove(2) }).then(value=>{ console.log(value) // 2 }) new MyPromise((reslove, reject)=>{ reject("我失败了") }).then(value=>{ console.log(value) }, reason=>{ console.log(reason) // 我失败了 }) -
问题
现在只是实现了同步情况下promise功能,但是promise是为了解决异步问题,所以现在要实现第二版:异步版
第二版:异步版本
-
需要解决的问题?
-
怎么解决异步? 在我们调用then方式时,promise状态还是
pending,resolve或者reject还未执行。那么我们就可以设置一个数组来保存then方法中的回调函数,在resolve或reject执行完成后遍历去执行then方法的回调函数。 -
为什么用数组来保存then方法?
在一个典型的 Promise 实现中,
then方法允许注册两个回调函数:一个用于处理成功状态(onResolve),另一个用于处理失败状态(onReject)。这两个参数都是可选的,用户可以只传递其中一个或两个。但是,在某些情况下,确实会有多个回调函数。例如,一个 Promise 实例可能会被多次调用
then方法,并传递不同的回调函数。这种情况下,每个回调函数都会被存储在对应的数组中,并在适当的时候依次执行。
-
-
代码
// 首先定义三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" // 定义MyPromise类 class MyPromise { // 定义constructor 并接受一个executor参数 constructor(executor){ // 初始状态 this.status = PENDING // 完成状态下的数据 this.value = undefined // 拒绝状态下的数据 this.reason = undefined // 定义两个数组用来存储pending状态下的then方法 this.onResolveList = [] this.onRejectList = [] // 定义resolve,reject方法用于接受对应数据和改变状态 const resolve = value=>{ this.value = value this.status = FULFILLED // 执行异步下的then方法中的onResolve回调 this.onResolveList.forEach(fn=>fn()) } const reject = reason=>{ this.reason = reason this.status = REJECTED // 执行异步下的then方法中的onReject回调 this.onRejectList.forEach(fn=>fn()) } // 接下来执行executor 执行异常直接抛出错误 try { executor(resolve, reject) } catch (error) { reject(error) } } // 定义then方法用于捕获结果并接受两个参数onResolve, onReject分别对应resolve和reject的数据回调 then(onResolve, onReject){ if(this.status === FULFILLED) { onResolve(this.value) } if(this.status === REJECTED) { onReject(this.reason) } // 若状态是pending情况下将回调函数保存到对应的数组中 if(this.status === PENDING) { this.onResolveList.push(()=>onResolve(this.value)) this.onRejectList.push(()=>onReject(this.reason)) } } } -
测试
new MyPromise((resolve, reject)=>{ setTimeout(resolve, 1000, 10) }).then(value=>{ console.log(value) // 10 }) new MyPromise((resolve, reject)=>{ setTimeout(reject, 1000, '失败了') }).then(value=>{ console.log(value) }, reason=>{ console.log(reason) // 失败了 }) -
问题 至此我们就实现了一个简单的MyPromise类,但是Promise还支持链式调用,所以我们还得继续第三版:链式调用
第三版:链式调用
-
需要解决的问题
new Promise((resolve, reject)=>{ setTimeout(resolve, 1000, 10) }) .then(value=>{ console.log(value) // 10 return {a: 20} }) .then() .then(value=>{ console.log(value) // {a: 20} return ()=>30 }) .then(value=>{ console.log(value) // f(){return 30} return new Promise((resolve, reject)=>{ setTimeout(resolve, 1000, 40) }) }) .then(value=>{ console.log(value) // 40 return new Promise((resolve, reject)=>{ setTimeout( resolve, 1000, new Promise((resolve, reject)=>{ setTimeout(resolve, 1000, 50) }) ) }) }) .then(value=>{ console.log(value) // 50 })观察上面Promise调用的示例我们可以发现下面几点
- then可以多次调用,说明then方法每次执行返回的是一个promise对象,而且不是上一个then返回的promise。因为执行自己的then没有任何意义
- then方法的回调函数可以省略,那么下次调用then方法时返回的是上一次then的返回值
- then方法可以返回函数和对象
- then方法返回是promise对象时,下次执行then方法时的回调数据是返回的promise对象执行then方法的回调数据而不是promise对象本身
- then方法返回的promise如果有嵌套promise,则需要完成递归调用回调返回最终的值
根据上面的几点我们可以编写下
-
代码
// 首先定义三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" // 定义MyPromise类 class MyPromise { // 定义constructor 并接受一个executor参数 constructor(executor){ // 初始状态 this.status = PENDING // 完成状态下的数据 this.value = undefined // 拒绝状态下的数据 this.reason = undefined // 定义两个数组用来存储pending状态下的then方法 this.onResolveList = [] this.onRejectList = [] // 定义resolve,reject方法用于接受对应数据和改变状态 const resolve = value=>{ this.value = value this.status = FULFILLED // 执行异步下的then方法中的onResolve回调 this.onResolveList.forEach(fn=>fn()) } const reject = reason=>{ this.reason = reason this.status = REJECTED // 执行异步下的then方法中的onReject回调 this.onRejectList.forEach(fn=>fn()) } // 接下来执行executor 执行异常直接抛出错误 try { executor(resolve, reject) } catch (error) { reject(error) } } // 定义then方法用于捕获结果并接受两个参数onResolve, onReject分别对应resolve和reject的数据回调 then(onResolve, onReject){ // 兼容无参 then().then(...){...} onResolve = typeof onResolve === 'function' ? onResolve : v => v onReject = typeof onReject === 'function' ? onReject : reason => { throw reason } const promise2 = new MyPromise((resolve, reject)=>{ // 处理完成状态下方法 const handleResolve = ()=>{ // then函数中onResolve回调参数的返回值 const x = onResolve(this.value) // 针对返回值的不同类型做处理 this.resolveCallbackReturn(promise2, x, resolve, reject) } // 处理失败下的方法 const handleReject = ()=>{ // then函数中onReject回调参数的返回值 const x = onReject(this.reason) this.resolveCallbackReturn(promise2, x, resolve, reject) } if(this.status === FULFILLED) handleResolve() if(this.status === REJECTED) handleReject() if(this.status === PENDING) { this.onResolveList.push(handleResolve) this.onRejectList.push(handleReject) } }) return promise2 } resolveCallbackReturn(promise2, x, reslove, reject){ // 如果新返回的 Promise 与上一个 then 返回的 Promise 相同,则抛出错误 if(promise2 === x) { return reject("不能返回自身") } // 返回若是promise对象 则调用then方法等待处理完成将结果回调 if(x instanceof MyPromise) { x.then(value=>{ // 针对多层Promise 递归获取最终值 this.resolveCallbackReturn(promise2, value, reslove, reject) }, reason=>{ // 针对多层Promise 递归获取最终值 this.resolveCallbackReturn(promise2, reason, reslove, reject) }) }else{ reslove(x) } } } -
测试
new MyPromise((resolve, reject)=>{ setTimeout(resolve, 1000, 10) }) .then(value=>{ console.log(value) // 10 return {a: 20} }) .then() .then(value=>{ console.log(value) // {a: 20} return ()=>30 }) .then(value=>{ console.log(value) // f(){return 30} return new MyPromise((resolve, reject)=>{ setTimeout(resolve, 1000, 40) }) }) .then(value=>{ console.log(value) // 40 return new MyPromise((resolve, reject)=>{ setTimeout( resolve, 1000, new MyPromise((resolve, reject)=>{ setTimeout(resolve, 1000, 50) }) ) }) }) .then(value=>{ console.log(value) // 50 })至此看起来我们能想到的都已经实现了,那么现在结合Promise/A+规范看下我们自己实现的MyPromise是否符合规范
跟着Promise/A+规范走
地址
Promise/A+规范地址:promisesaplus.com/
适配规范
翻看了规范地址后可以了解到
-
2.1 Promise状态不能改变
-
示例
const promise = new MyPromise((resolve, reject) => { resolve(1); reject(new Error('Rejected')); // 这里状态会逆转为 rejected }); -
解决
// 定义resolve,reject方法用于接受对应数据和改变状态 const resolve = value=>{ // PENDING 只能转成FULFILLED或REJECTED if(this.status !== PENDING) return this.value = value this.status = FULFILLED // 执行异步下的then方法中的onResolve回调 this.onResolveList.forEach(fn=>fn()) } const reject = reason=>{ // PENDING 只能转成FULFILLED或REJECTED if(this.status !== PENDING) return this.reason = reason this.status = REJECTED // 执行异步下的then方法中的onReject回调 this.onRejectList.forEach(fn=>fn()) }
-
-
2.3.1: 如果
promise和x引用相同的对象,promise则以 aTypeError作为理由拒绝-
我们的
// 如果新返回的 Promise 与上一个 then 返回的 Promise 相同,则抛出错误 if(promise2 === x) { return reject("不能返回自身") } -
解决
if(promise2 === x) { return reject(new TypeError("不能返回自身")) }
-
-
3.1: 这里的“平台代码”指的是引擎、环境和 Promise 实现代码。实际上,此要求确保在调用事件循环之后异步执行
onFulfilled,并使用新的堆栈。这可以通过“宏任务”机制(例如或 )或“微任务”机制来实现。-
示例
new MyPromise((resolve, reject)=>{ resolve(1) }).then(value=>{ console.log(value) }) console.log(2) /** * 输出结果 * 1 * 2 */ -
解决
// 处理完成状态下方法 const handleResolve = ()=>{ // 模拟异步任务 queueMicrotask(()=>{ // then函数中onResolve回调参数的返回值 const x = onResolve(this.value) // 针对返回值的不同类型做处理 this.resolveCallbackReturn(promise2, x, resolve, reject) }) } // 处理失败下的方法 const handleReject = ()=>{ queueMicrotask(()=>{ // then函数中onReject回调参数的返回值 const x = onReject(this.reason) this.resolveCallbackReturn(promise2, x, resolve, reject) }) }
-
-
2.3: 这种对 thenable 的处理允许 Promise 实现进行互操作,只要它们公开 Promises/A+ 兼容的
then方法。它还允许 Promises/A+ 实现用合理的then方法“同化”不合格的实现。 -
2.3.3.2: 如果检索属性
x.then导致抛出异常e,请拒绝promise作为e原因 -
2.3.3.3 如果同时
resolvePromise调用 和rejectPromise,或者对同一参数进行多次调用,则第一个调用优先,并且忽略任何后续调用。-
示例
// 若then返回的对象包含then的实现规范,同样需要实现then的最终回调 new Promise((resolve, reject)=>{ resolve(1) }).then(value=>{ return { a: 1, then(resolve, reject){ setTimeout(resolve, 1000, this.a) // 会忽略 setTimeout(resolve, 1000, this.a + 1) } } }).then(value=>{ console.log(value) // 1 }) -
实现
resolveCallbackReturn(promise2, x, resolve, reject){ // 如果新返回的 Promise 与上一个 then 返回的 Promise 相同,则抛出错误 if(promise2 === x) { return reject(new TypeError("不能返回自身")) } // 返回若是promise对象 则调用then方法等待处理完成将结果回调 if(x && (typeof x === 'object' || typeof x === 'function')){ // 当调用 then 方法返回的对象或函数中的 then 方法时,我们需要确保只调用一次 resolvePromise 或 rejectPromise。这是因为规范要求 then 方法只能调用一次,如果多次调用将被忽略。 let called = false // 兼容thenable try { const then = x.then if(typeof then === 'function') { then.call(x, value=>{ if (called) return called = true this.resolveCallbackReturn(promise2, value, resolve, reject) }, reason=>{ if (called) return called = true // this.resolveCallbackReturn(promise2, reason, resolve, reject) reject(reason) }) }else{ if (called) return called = true resolve(x) } }catch (error) { if (called) return called = true reject(error) } }else{ resolve(x) } }
-
代码测试
安装
yarn add promises-aplus-tests -D
测试配置
MyPromise.deferred = function () {
var result = {}
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
命令
npx promises-aplus-tests path
最终代码
// 首先定义三种状态
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
// 定义MyPromise类
class MyPromise {
// 定义constructor 并接受一个executor参数
constructor(executor){
// 初始状态
this.status = PENDING
// 完成状态下的数据
this.value = undefined
// 拒绝状态下的数据
this.reason = undefined
// 定义两个数组用来存储pending状态下的then方法
this.onResolveList = []
this.onRejectList = []
// 定义resolve,reject方法用于接受对应数据和改变状态
const resolve = value=>{
// PENDING 只能转成FULFILLED或REJECTED
if(this.status !== PENDING) return
this.value = value
this.status = FULFILLED
// 执行异步下的then方法中的onResolve回调
this.onResolveList.forEach(fn=>fn())
}
const reject = reason=>{
// PENDING 只能转成FULFILLED或REJECTED
if(this.status !== PENDING) return
this.reason = reason
this.status = REJECTED
// 执行异步下的then方法中的onReject回调
this.onRejectList.forEach(fn=>fn())
}
// 接下来执行executor 执行异常直接抛出错误
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
// 定义then方法用于捕获结果并接受两个参数onResolve, onReject分别对应resolve和reject的数据回调
then(onResolve, onReject){
// 兼容无参 then().then(...){...}
onResolve = typeof onResolve === 'function' ? onResolve : v => v
onReject = typeof onReject === 'function' ? onReject : reason => { throw reason }
const promise2 = new MyPromise((resolve, reject)=>{
// 处理完成状态下方法
const handleResolve = ()=>{
// 模拟异步任务
queueMicrotask(()=>{
try {
// then函数中onResolve回调参数的返回值
const x = onResolve(this.value)
// 针对返回值的不同类型做处理
this.resolveCallbackReturn(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
// 处理失败下的方法
const handleReject = ()=>{
queueMicrotask(()=>{
try {
// then函数中onReject回调参数的返回值
const x = onReject(this.reason)
this.resolveCallbackReturn(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if(this.status === FULFILLED) handleResolve()
if(this.status === REJECTED) handleReject()
if(this.status === PENDING) {
this.onResolveList.push(handleResolve)
this.onRejectList.push(handleReject)
}
})
return promise2
}
resolveCallbackReturn(promise2, x, resolve, reject){
// 如果新返回的 Promise 与上一个 then 返回的 Promise 相同,则抛出错误
if(promise2 === x) {
return reject(new TypeError("不能返回自身"))
}
// 返回若是promise对象 则调用then方法等待处理完成将结果回调
if(x && (typeof x === 'object' || typeof x === 'function')){
// 当调用 then 方法返回的对象或函数中的 then 方法时,我们需要确保只调用一次 resolvePromise 或 rejectPromise。这是因为规范要求 then 方法只能调用一次,如果多次调用将被忽略。
let called = false
// 兼容thenable
try {
const then = x.then
if(typeof then === 'function') {
then.call(x, value=>{
if (called) return
called = true
this.resolveCallbackReturn(promise2, value, resolve, reject)
}, reason=>{
if (called) return
called = true
// this.resolveCallbackReturn(promise2, reason, resolve, reject)
reject(reason)
})
}else{
if (called) return
called = true
resolve(x)
}
}catch (error) {
if (called) return
called = true
reject(error)
}
}else{
resolve(x)
}
}
}