异步编程 -- 手写Promise初体验
Promise模拟,尝试手写Promise
思考
Promise通过new Promise((resolve,reject)=>{}) 使用,并通常会传入一个函数,这个函数有两个参数,一个是resolve,一个是reject,resolve应该是一个可以解决事情的函数,reject应该是一个当事情解决失败时的处理函数,所以Promise应该是一个类,此类的构造器应该传入一个执行器,用来执行处理函数,并且应该有两个处理函数resolve和reject
-
Promise有哪些属性和方法?
从平常使用中可以知道,Promise有如下几个方法供我们使用:
- resolve(value),用来处理事情,此方法接收一个参数,可以在Promise实例中直接使用,所有应该考虑其this指向问题
- reject(reason),用来处理不能处理事情时的情况,此方法接收一个参数,与resolve相同,可以在Promise实例中直接使用,应该考虑其this指向
- then(successCallback,failCallback),这个用来处理当实例中的操作完成后进行的后续操作,可以多次链式调用,此方法接收两个参数,一个是成功的回调,一个是失败的回调,参数都是可选的,每次返回的都是一个Promise实例
- finally(callbacl),当Promise的事情处理完后,会进入到此方法,此方法接收一个回调,此方法也是返回一个Promise实例,因此,理论上finally后面还是可以继续链式调用then方法的
- catch(callback),当Promise实例执行发生错误时,会进入到此方法,此方法接收一个回调参数来处理错误
- Promise.resolve(value),这是一个静态方法,只能直接通过Promise类使用,不能通过实例调用,接收一个参数,返回Promise实例
- Promise.all(array),这也是一个静态方法,只能直接通过Promise类使用,不能通过实例调用,接收一个数组参数,返回Promise实例
class MyPromiseRepeat{ constructor(executor){ executor(this.resolve,this.reject) } resolve(value){} reject(reason){} then(successCallback,failCallback){} static resolve(value) static all(array) finally(callback){} catch(callback){} }
-
Promise怎么进行过程控制的?
通过查看Promise原码,发现其使用了几个状态值,pending表示事情未完成成,fulfilled表示事情完成,rejected表示事情失败,那么可以定义这几个常量先:
const PENDING='pending'
const FULFILLED='fulfilled'
const REJECTED='rejected'
-
那这些状态应该是怎么改变的呢?
首先,状态默认是PENDING状态,当事情处理完成后,状态会变成FULFILLED,当事情失败时,状态会变成REJECTED,那么肯定是有一个变量来储存这个状态了,就叫在Promise中再加一个Status吧
Status=PENDING
后续的resolve和reject就可以改变状态,请继续看
-
resolve()
想一下resolve方法都是干嘛的,resolve的作用有几个:
- 判断状态,resolve方法,你可以调用多次,但我不会执行多次,只会执行第一次,因为在这里,要判断判断是否是PENDING,如果不是,那么直接返回,因为不是PENDING表示已经处理过事情了,不需要再处理了,这样讲可能有点牵强,但是多理解理解应该会懂
- 更改状态,经过resolve后,Promise的状态就会改变,在resolve中会改变成FULFILLED
- 赋值,怎么说?当然是把传入的参数经过处理后存在起来呀,存哪里,那就在Promise中再添加一个value吧
- 执行回调,这个又怎么说?因为在then方法中会传入回调,那这个回调是直接调用的吗?不一定,当同步执行时,可以直接调用,但是是异步时,就不行了,所有需要把传入then的回调存储起来,那这些回调在什么时候执行?有两个地方,一个就是在resolve中,当然,这里指的回调是成功回调,另一个就是reject了,reject执行失败回调
// 定义全局变量value,用来存储处理方法resolve的参数,默认为undefined
value=undefined
resolve=(value)=>{
// 判断状态
if(this.Status!=PENDING) return
// 更改状态
this.Status=FULFILLED
// 赋值
this.value=value
// 执行成功回调
while(this.successCallback.length) this.successCallback.shift()()
}
-
reject()
与resolve一样,需要做以下几件事:
- 判断状态,不是PENDING将直接返回
- 更改状态
- 赋值,reject传入的值即发生错误的原因
- 执行错误回调
// 定义全局变量reason,用来存储错误的原因,默认值为undefined
reason=undefined
reject=(reason)=>{
if(this.Status!=PENDING)return
this.reason=reason
this.Status=REJECTED
while(this.failCallback.length) this.failCallback.shift()()
}
-
then()
then方法接收两个参数,一个successCallback,一个failCallback,参数可选,, 在then方法需要对参数进行判断,首先是没有传入参数时的处理情况,默认是上一次处理的结果,上一处理失败则会是undefined,因为then方法可以链式调用,且方法可能有异步函数,当传入参数为异步操作时,需要将传入的异步回调用数组存储起来,then方法返回的是Promise对象
// 定义两个数组来存储异步回调
successCallback=[]
failCallback=[]
then(successCallback,failCallback){
// 让参数可选,
successCallback=successCallback?successCallback:value=>value
failCallback=failCallback?failCallback:reason=>{throw reason}
// 返回Promise实例
let promise2= new MyPromiseRepeat((resolve,reject)=>{
// 成功时有执行
if(this.Status==FULFILLED){
// 为了能够调用此Promise实例,采用setTimeout来保证拿到promise2
setTimeout(()=>{
// tryCatch截取错误并交给reject
try {
let x=successCallback(this.value)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
// 失败时执行
}else if(this.Status==REJECTED){
setTimeout(()=>{
try {
let x=failCallback(this.reason)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
}else{// 异步操作还没有完成时执行
this.successCallback.push(()=>{
setTimeout(()=>{
try {
let x=successCallback(this.value)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
})
this.failCallback.push(()=>{
setTimeout(()=>{
try {
let x=failCallback(this.reason)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
})
}
})
return promise2
}
// resolvePromise方法
function resolvePromise(promise2,x,resolve,reject){
// 这里是识别自调用
if(promise2===x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 当异步操作则then
if(x instanceof MyPromiseRepeat){
x.then(resolve,reject)
}else{
resolve(x)
}
}
-
MyPromiseRepeat.resolve()
与resolve方法不同,Promise.resolve是把传入的参数转换成Promise对象并返回,所以这个很简单,当传入的参数不是Promise对象时,返回一个新的Promise实例,这个实例resolve了传入的参数,当传入的参数是Promise实例时,直接返回参数
static resolve(value){
if(value instanceof MyPromiseRepeat) return value
else{
// 不是Promise实例则返回新Promise实例
return new MyPromiseRepeat((resolve)=>{
resolve(value)
})
}
}
-
Promise.all()
当所有参数数组中的情事都进行完成并成功后,算成功,返回Promise实例,当有一件事情失败时,就算失败,返回Promise实例,这里需要注意的是怎么去判断数组中的事情是否都执行完成,可以用一个数组 result去存储事情结果,遍历参数数组,并用一个数count来记住添加到result中的结果,如果数组项是Promise实例,则通过此项的then方法去向result中添加一个事情,count++,如果不是Promise对象,则直接添加到result中,count++,这样就能保证异步事情都可以在处理完成后把结果存储到result中,当count==参数数组的长度时,就表示参数中的所有异步任务都执行完成并添加到了result中,这时就可以resolve(result)来返回结果了
static all(array){
// 存储结果
let result=[]
// 记数count
let resultCompletedCount=0
// 返回新Promise实例
return new MyPromiseRepeat((resolve,reject)=>{
// 添加结果到result的方法
function AddResult(index,value){
result[index]=value
resultCompletedCount++
// 当result长度和参数array一样长时,resolve(result)
if(resultCompletedCount==array.length){
resolve(result)
}
}
// 遍历参数数组,处理每一项,是Promise实例则调用其then方法来获取结果,不是Promise实例则直接添加到result中
for(let i =0;i<array.length;i++){
let current= array[i]
if(current instanceof MyPromiseRepeat){
// 为能保证全部执行,异步操作完成后再把结果添加到result
current.then(value=>AddResult(i,value),reason=>reject(reason))
}else{
AddResult(i,array[i])
}
}
})
}
-
finally()
finally方法其实就是一个then方法,只是它只接收一个回调,方法内部调用then方法,根据callback的执行结果来决定返回结果
// ...,
finally(callback){
return this.then(value=>{
return MyPromiseRepeat.resolve(callback()).then(()=>value)
},reason=>{
return MyPromiseRepeat.resolve(callback()).then(()=>{throw reason})
})
}
-
catch方法
catch方法也和then方法一样,只接收第二个参数,即失败处理回调
catch(callback){
return this.then(undefined,callback)
}
代码编写
通过上面的分析,写出如下代码:
// 定义三个状态常量
const PENDING='pending'
const FULFILLED='fulfilled'
const REJECTED='rejected'
// 创建Promise类
class MyPromiseRepeat{
// 构造函数接收一个执行器
constructor(executor){
//用tryCatch截取错误,并交给reject处理
try {
executor(this.resolve,this.reject)
} catch (error) {
this.reject(error)
}
}
// resolve参数
value=undefined
// 失败原因
reason=undefined
// 状态
Status=PENDING
// 成功回调存储
successCallback=[]
// 失败回调存储
failCallback=[]
// reslove方法
resolve=(value)=>{
// 判断状态
if(this.Status!=PENDING) return
// 更改状态
this.Status=FULFILLED
// 赋值
this.value=value
// 执行成功回调
while(this.successCallback.length) this.successCallback.shift()()
}
reject=(reason)=>{
if(this.Status!=PENDING)return
this.reason=reason
this.Status=REJECTED
while(this.failCallback.length) this.failCallback.shift()()
}
then(successCallback,failCallback){
// 让参数可选,
successCallback=successCallback?successCallback:value=>value
failCallback=failCallback?failCallback:reason=>{throw reason}
// 返回Promise实例
let promise2= new MyPromiseRepeat((resolve,reject)=>{
// 成功时有执行
if(this.Status==FULFILLED){
// 为了能够调用此Promise实例,采用setTimeout来保证拿到promise2
setTimeout(()=>{
// tryCatch截取错误并交给reject
try {
let x=successCallback(this.value)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
// 失败时执行
}else if(this.Status==REJECTED){
setTimeout(()=>{
try {
let x=failCallback(this.reason)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
}else{// 异步操作还没有完成时执行
this.successCallback.push(()=>{
setTimeout(()=>{
try {
let x=successCallback(this.value)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
})
this.failCallback.push(()=>{
setTimeout(()=>{
try {
let x=failCallback(this.reason)
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
this.reject(error)
}
},0)
})
}
})
return promise2
}
finally(callback){
return this.then(value=>{
return MyPromiseRepeat.resolve(callback()).then(()=>value)
},reason=>{
return MyPromiseRepeat.resolve(callback()).then(()=>{throw reason})
})
}
catch(callback){
return this.then(undefined,callback)
}
static resolve(value){
if(value instanceof MyPromiseRepeat) return value
else{
// 不是Promise实例则返回新Promise实例
return new MyPromiseRepeat((resolve)=>{
resolve(value)
})
}
}
static all(array){
let result=[]
let resultCompletedCount=0
return new MyPromiseRepeat((resolve,reject)=>{
function AddResult(index,value){
result[index]=value
resultCompletedCount++
// 当result长度和参数array一样长时,resolve(result)
if(resultCompletedCount==array.length){
resolve(result)
}
}
for(let i =0;i<array.length;i++){
let current= array[i]
if(current instanceof MyPromiseRepeat){
// 为能保证全部执行,异步操作完成后再把结果添加到result
current.then(value=>AddResult(i,value),reason=>reject(reason))
}else{
AddResult(i,array[i])
}
}
})
}
}
function resolvePromise(promise2,x,resolve,reject){
// 这里是识别自调用
if(promise2===x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 当异步操作则then
if(x instanceof MyPromiseRepeat){
x.then(resolve,reject)
}else{
resolve(x)
}
}
module.exports=MyPromiseRepeat
测试结果
// index.js
const MyPromise = require('./MyPromiseRepeat.js')
let p1=function(){
return new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('p1')
},2000)
})
}
let p2=function(){
return new MyPromise((resolve,reject)=>{
resolve('p2')
// reject('失败')
})
}
MyPromise.all(['a','b',p1(),p2(),'c']).then(res=>console.log(res),reason=>console.log(reason)) //此处对应输出['a','b','p1','p2','c']
// MyPromise.resolve('1').then((value)=>console.log(value))
// MyPromise.resolve(p1()).then((value)=>console.log(value))
p2().catch((reason)=>{
console.log(reason)
}).finally(()=>{
console.log('finally')
// 这里返回p1()但是继续then的话,拿到的还是p2的结果"p2",因为在finally中返回的就是finally的调用者p2的实例,如果p2错误,则拿到的是undefined
return p1()
}).then(value=>console.log(value),reason=>console.log(reason))//此处对应输出finally p2
// 最终输出:
// finally
// ['a','b','p1','p2','c']
// p2
// 如果p2 reject了,Promise.all则失败,所以输出'失败',后面catch也会捕获错误,输出'失败',finally不受影响,输出'finally',finally.then则是输出undefined,因为p2失败了,没有结果传递下来,则最终输出:
// 失败
// 失败
// finally
// undefined