本篇思路学习自up主技术蛋老师
一、实现思路
要实现一个 Promise,那就得了解它的特性,以及知道它是如何执行的。
1. Promise 是如何执行的:
- 新建 Promise 对象 = 创建一个异步函数。
- 这个异步函数中带有 resolve 与 reject 两个函数形参。
- 执行这个异步函数内代码,并返回成功与失败状态和回调。
- 在 .then 中执行这个回调并返回一个新的 Promise,同时支持链式编程。
2. Promise 特性:
- 主动抛出错误,会调用 reject() 方法回调。
- .then 中传入非函数参数,不会报错。
3. 实现思路:
- 实例化 Promise 对象:使用构造函数或者 class 实现。
- 实例化时执行函数,并带有 resolve reject 回调:使用构造器执行成功与失败回调。
- 保持状态,且状态一旦改变不会更改:padding(等待) fulfilled(成功) reject(失败)。
- 使用 resolve 与 reject 改变当前 Promise 状态并拿到执行参数:使用两个实例方法执行。
- then 执行回调结果。
- 异步执行:使用定时器执行 .then 回调。
- 链式执行:让 .then 返回一个新的 Promise 来实现链式编程。
- 使用 try-catch 来判断代码有无主动抛出一个错误。
- 对 .then 中传入的参数进行非函数判断,如果不为函数则将其回调赋值为一个空对象。
二、手搓 Promise
1. 新建 Commitment 类
使用 ES6 Class 新建 Commitment 类,并在构造器中传入默认参数 func,用来执行实例化时使用的函数。
且这个函数带有 resolve 与 reject 两个回调函数作为参数。
class Commitment{
construcotr(func){
// 使用 bind 绑定调用时作用域
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(){}
reject(){}
}
2. 使用 static 作为 Promise 状态
添加三种状态表示当前这个 Promise 的状态,并初始化当前状态为 pending。并添加 result 属性保存当前 Promise 执行的回调参数。
在 resolve 与 reject 中更改当前 Promise 状态,并将传入的参数保存起来。
class Commitment{
static PENDING = "待定"
static FULFILLED = "成功"
static REJECTED = "拒绝"
construcotr(func){
this.status = Commitment.PENDING
this.result = null
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result){
if (this.status === Commitment.PENDING) {
this.status = Commitment.FULFILLED
this.result = result
}
}
reject(result){
if (this.status === Commitment.PENDING) {
this.status = Commitment.REJECTED
this.result = result
}
}
}
3. 使用 .then 执行回调
添加 then() 方法来执行回调,通过判断当前状态来决定执行成功还是失败,并将 result 属性传入执行。
then(onFULFILLED, onREJECTED){
if(this.status === Commitment.FULFILLED){
onFULFILLED(this.result)
}else if(this.status === CCommitment.REJECTED){
onREJECTED(this.result)
}
}
4. 实现异步编程
其实只需要在 then() 执行回调方法的外面嵌套上一层定时器就可以了。
then(onFULFILLED, onREJECTED){
setTimeout(() => {
if(this.status === Commitment.FULFILLED){
onFULFILLED(this.result)
}else if(this.status === CCommitment.REJECTED){
onREJECTED(this.result)
}
})
}
5. 实现链式编程
这个也简单,不是很难理解,其实只要将 then() 方法的执行结果用一个新的 Promise 返回出去就行了。
then(onFULFILLED, onREJECTED){
// 返回一个新的 Promise 实现链式编程。
return new Commitment((resolve, reject) => {
setTimeout(() => {
if(this.status === Commitment.FULFILLED){
// 这里必须调用 resolve 方法包裹,诺不然新的 Promise
// 会接收不到当前 Promise 的返回值
resolve(onFULFILLED(this.result))
}else if(this.status === CCommitment.REJECTED){
//
reject(onREJECTED(this.result))
}
})
})
}
三、完善 Promise 特性
1. 添加 try-catch 判断代码有无主动抛出错误
只需要用 try-cathc 包括住 func() 的执行,并在 catch 中调用 reject() 方法就可。
constructor(func){
try {
func(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
// 如果有抛出错误,则直接调用 reject() 回调
this.reject(error.message)
}
}
2. 在 .then 执行前添加非函数判断
需要判断用户使用时传入的是否不是一个回调函数。
这里使用三元表达式进行一个简单的判断赋值就完全没有问题了。
then(onFULFILLED, onREJECTED) {
return new Commitment((resolve, reject) => {
// 判断成功回调是否不是回调函数,如果不是则直接返回 value 值
onFULFILLED = typeof onFULFILLED === "function" ? onFULFILLED : value => value
// 失败回调就直接报错
onREJECTED = typeof onREJECTED === "function" ? onREJECTED : err => { throw err}
setTimeout(() => {
if (this.status === Commitment.FULFILLED) {
resolve(onFULFILLED(this.result))
} else if (this.status === Commitment.REJECTED) {
reject(onREJECTED(this.result))
}
}, 0)
})
}
四、最终代码
class Commitment{
static PENDING = '待定';
static FULFILLED = '成功';
static REJECTED = '拒绝';
constructor(func){
this.status = Commitment.PENDING
this.result = null
try {
func(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result){
if(this.status === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
}
}
reject(result){
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECTED
this.result = result
}
}
then(onFULFILLED, onREJECTED){
return new Commitment((resolve,reject) => {
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : () => {}
onREJECTED = typeof onFULFILLED === 'function' ? onFULFILLED : () => {}
setTimeout(() => {
if(this.status === Commitment.FULFILLED){
// 这里必须调用 resolve 方法包裹,诺不然新的 Promise
// 会接收不到当前 Promise 的返回值
// 首先会执行onFULFILLED,也就是then中传来的方法,然后用此方法的返回值传个resolve方法进行更新
resolve(onFULFILLED(this.result))
}
if(this.status === Commitment.REJECTED){
reject(onREJECTED(this.result))
}
});
})
}
}
const promise = new Commitment((resolve,reject) => {
setTimeout(() => {
resolve("手写Promise")
});
})
promise.then((result) => {
console.log("第一个then::", result)
return 'return给第二个then'
}).then(res =>{
console.log('第二个then:',res)
}).then(res =>{
console.log('第三个then:',res)
})
调试的log
console.log('第1步')
const promise = new Commitment((resolve,reject) => {
console.log('第2步')
setTimeout(() => {
resolve("手写Promise")
});
})
console.log('第3步')
promise.then((result) => {
console.log("第一个then::", result)
return 'return给第二个then'
}).then(res =>{
console.log('第二个then:',res)
}).then(res =>{
console.log('第三个then:',res)
})
console.log('第4步')
// 第1步
// 第2步
// 第3步
// 第4步
// 第一个then:: 手写Promise
// 第二个then: return给第二个then
// 第三个then: undefined
Promise.resolve()
Promise.resolve = (value => {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then((res) => {
resolve(res)
}, e => {
reject(e)
})
} else {
resolve(value)
}
})
})
Promise.reject()
Promise.reject = (value => {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then((res) => {
resolve(res)
}, e => {
reject(e)
})
} else {
reject(value)
}
})
})
Promise.all()
all(promiseList){
return new Promise((resolve, reject) => {
let count = 0
let returnArr = []
for (let i = 0; i < promiseList.length; i++) {
promiseList[i].then(res => {
count++;
returnArr.push(res)
if (count === promiseList.length) {
resolve(returnArr)
}
}, (err => {
reject(err)
}))
}
})
}
Promise.race()
race(promiseList){
return new Promise((resolve, reject) => {
for (let i = 0; i < promiseList.length; i++) {
promiseList[i].then(res => {
resolve(res)
}, (err => {
reject(err)
}))
}
})
}