一、实现思路
要实现一个 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) })) } }) }