前言: 对于一件东西最好的理解就是能够清楚知道他是如何实现的,正所谓知其然知其所以然,开始吧
1、关于promise的介绍
首先,我们知道,Promise是率先由commonJs提出来的一种异步解决方案,后来在ES6中正式引入,成为我们处理异步的强有力的工具。
我们通过自己手动的方式进行梳理一遍Promise是实现的过程,让我们清楚它的内如是如何是实现的
2、实现Promise的过程
1、promise 是一个类,在执行这个类的时候会传入一个执行器executor,该类一经创建会立即执行。
2、Promise 一共有三种状态,等待(pedding)、成功(fulfilled)、失败(rejected)。
-
在成功状态的时候执行resolve 状态: pedding -> fulfilled
-
在失败状态的时候执行reject 状态: pedding -> rejected
-
这些状态一经改变不可更改
3、创建类时传入的执行器中会有两个函数参数,在执行该执行器的时候,这两函数会执行其一
4、在Promise类中有一个then方法,它是用来判断返回状态的,他回传入两个函数作为参数,successCallBack(成功回调函数)、failCallBack(失败回调函数)
- successCallBack是执行了resolve函数后调用的函数,它的参数就是resolve的传入参数,是执行成功的结果
- failCallBack 是执行力了reject函数后调用的函数,它的参数就是reject的传入参数,是执行失败的原因
下面用代码实现一下 (下面都是以MyPromise来展示)
代码1:
// 标记类的三种执行状态
const PADDING = 'pedding' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败
class MyPromise {
// 构造函数
constructor (executor) {
executor(this.resolve, this.reject) // 传入的执行器
}
// 用来记录该类当前的状态
status = PADDING
// 记录执行成功的结果
result = undefined
// 记录执行失败的原因
reason = undefined
// 保存处于等待状态的成功回调函数的结果集
successCallBack = undefined
// 保存处于等待状态的失败回调函数的原因集
failCallBack = undefined
// 执行成功调用的函数,不使用旧的function是关于this指向的当前Promise对象中的问题
resolve = result => {
if (this.status === FULFILLED) return
this.status = FULFILLED
this.result = result
this.successCallBack && this.successCallBack(this.result)
}
// 执行失败调用的函数,
reject = reason => {
if (this.status === REJECTED) return
this.status = REJECTED
this.reason = reason
this.failCallBack && this.failCallBack(this.reason)
}
// then方法用来执行回调函数
then (successCallBack, failCallBack) {
if (this.status === FULFILLED) {
successCallBack(this.result)
} else if (this.status === REJECTED) {
failCallBack(this.reason)
} else {
// 这里将等待状态的回调函数保存起来
this.successCallBack = successCallBack
this.failCallBack = failCallBack
}
}
}
这里就基本实现了上述陈述的几条步骤,但是还有一些功能要进行修改
5、当我们连续执行then方法的时候,对于异步处理结果而言,要将等待时候的成功回调函数或者失败回调存储起来(数组 - 多次调用then),在执行成功/失败后调用
下面将代码1中的一些地方进行改动
代码2:
// 保存处于等待状态的成功回调函数的结果集
successCallBack = undefined
// 保存处于等待状态的失败回调函数的原因集
failCallBack = undefined
为了能够多次同步调用then方法,我们将它改为数组,方便存储多个回调函数
代码3:
// 保存处于等待状态的成功回调函数的结果集
successCallBack = []
// 保存处于等待状态的失败回调函数的原因集
failCallBack = []
还有 then 方法 、resolve 和reject也要修改回调函数保存方式
代码4:
// 执行成功调用的函数,不使用旧的function是关于this指向的问题
resolve = result => {
if (this.status === FULFILLED) return
this.status = FULFILLED
this.result = result
// 这里判断数组长度,再向前依次推出回调函数去执行
while (this.successCallBack.length) this.successCallBack.shift()(this.result)
}
// 执行失败调用的函数,
reject = reason => {
if (this.status === REJECTED) return
this.status = REJECTED
this.reason = reason
// 当执行多次then失败时,通过数组调用的回调函数依次朝前推出执行回调函数
while (this.failCallBack.length) this.failCallBack.shift()(this.reason)
}
then (successCallBack, failCallBack) {
if (this.status === FULFILLED) {
successCallBack(this.result)
} else if (this.status === REJECTED) {
failCallBack(this.reason)
} else {
// 这里将等待状态的回调函数保存起来
this.successCallBack.push(successCallBack)
this.failCallBack.push(failCallBack)
}
}
6、对于then方法的链式调用,可以通过返回一个Promise的实例来实现,在每个Promise中都有then方法,通过将返回值传递到下一个Promise中,这样我们就可以实现then方法的链式调用了,这里改变一下then方法的实现
在then方法中,每次返回一个新的Promise对象
代码5:
then (successCallBack, failCallBack) {
let newPromise = new MyPromise ( (resolve, reject) => {
if (this.status === FULFILLED) {
successCallBack(this.result)
} else if (this.status === REJECTED) {
failCallBack(this.reason)
} else {
// 这里将等待状态的回调函数保存起来
this.successCallBack.push(successCallBack)
this.failCallBack.push(failCallBack)
}
})
return newPromise
}
7、值得注意的是,对于上一个链式的回调函数,如果有返回值,要进行区分是普通值还是异步操作类(例如:Promise)的值,如果是普通值,直接返回, 如果是Promise之类的,等待它的结果进行成功或者失败的执行。这里还要对then方法进行进一步的升级。
代码6:
then (successCallBack, failCallBack) {
let newPromise = new MyPromise ( (resolve, reject) => {
if (this.status === FULFILLED) {
// 获取成功回调的返回值
const x = successCallBack(this.result)
// checkePromise方法是用来判断上一个Promise返回的是普通值还是Promise值,并对其做进一步的处理
checkeValue (x,resolve, reject)
} else if (this.status === REJECTED) {
// 获取失败回调的返回值
const err = failCallBack(this.reason)
// checkePromise方法是用来判断上一个Promise返回的是普通值还是Promise值,并对其做进一步的处理
checkeValue (err,resolve, reject)
} else {
this.successCallBack.push(successCallBack)
this.failCallBack.push(failCallBack)
}
})
return newPromise
}
// 这个方法定义在MyPromise类之外
function checkeValue (val,resolve, rejecct) {
// 返回的是Promise对象
if (val instanceof MyPromise) {
val.then( val => resolve(val), reason => {throw Error(reason)})
// val.then(resolve, rejecct )
} else {
// 返回的是普通值
resolve(val)
}
}
8、还有,对于调用者写的回调函数,我们也要进行错误的抓取,在抛错的情况下进行reject失败返回(两处:执行器函数的执行,then的成功/失败回调),使用try catch 进行捕获。
代码7:
constructor (executor) {
// 捕获代码的错误(执行器出)
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
// 成功回调函数处, 将then方法中的成功回调函数处替换此代码
try {
const x = successCallBack(this.result)
checkeValue (x,resolve, reject)
} catch (e) {
reject(e)
}
// 失败回调函数处, 将then方法中的失败回调函数处替换此代码
try {
const err = failCallBack(this.reason)
checkeValue (err,resolve, reject)
} catch (e) {
reject(e)
}
9、对于then函数的回调是否存在,如果存在,就执行,如果不存在,要将返回值或者失败原因向下一个Promise回调函数中传递,例如 new Promise.then().then().then(),在最后一个then中获取执行结果。
对于then方法返回的Promise对象中,为了判断自身调用自身的情况,使用了内部定义的变量(newPromise),但是对于异步操作来说,同步执行代码的情况下,无法立即获取未执行的异步结果,所有要将成功/失败回调函数的执行进行异步处理
then方法终于写完了!!!
代码8:
then (successCallBack, failCallBack) {
// 这里对没有传入参数进行参数向下传递
successCallBack = successCallBack ? successCallBack : val => val
failCallBack = failCallBack ? failCallBack : err => { throw err }
let newPromise = new MyPromise ((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout (() => {
// 捕获成功回调函数的错误,返回给下一个Promise错误回调
try {
const x = successCallBack(this.result)
// checkePromise方法是用来判断上一个Promise返回的是普通值还是Promise值,并对其做进一步的处理
// 为了防止自身调用自身的情况,需要添加一个对自身的判断 --> promise 和 val的判读
// 这里有一个问题:promise参数是当前结果返回所得,这里要加入异步操作来排队执行 --> 上面setTimeOut操作
checkeValue (newPromise,x,resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout (() => {
try {
const err = failCallBack(this.reason)
checkeValue (newPromise,err,resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else {
this.successCallBack.push(() => {
setTimeout (() => {
// 捕获成功回调函数的错误,返回给下一个Promise错误回调
try {
const x = successCallBack(this.result)
// checkePromise方法是用来判断上一个Promise返回的是普通值还是Promise值,并对其做进一步的处理
// 为了防止自身调用自身的情况,需要添加一个对自身的判断 --> promise 和 val的判读
// 这里有一个问题:promise参数是当前结果返回所得,这里要加入异步操作来排队执行 --> 上面setTimeOut操作
checkeValue (newPromise,x,resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.failCallBack.push( () => {
setTimeout (() => {
try {
const err = failCallBack(this.reason)
checkeValue (promise,err,resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return newPromise
}
// -------------------------------------------------------------
// 下面的checkeValue也添加了判断条件
function checkeValue(promise,val,resolve, rejecct) {
if ( promise === val) {
throw TypeError('MyPromise into an endless loop')
}
// 返回的是Promise对象
if (val instanceof MyPromise) {
val.then( val => resolve(val), reason => {throw Error(reason)})
// val.then(resolve, rejecct )
} else {
// 返回的是普通值
resolve(val)
}
}
3、这里关于Promise的基本功能就写完了,还有一些Promise 的常用方法下面进行补充
Promise中的静态方法 -- all 方法
代码9:
/**
* 思路:
* 1、首先该方法会返回一个Promise对象,在成功对调中获取到一个结果数组
* 2、要对数组中的每一个值进行处理,对于普通值,直接执行回调,
* 对于Promise,则是先获取Promise结果,根据是否成功执行成功回调或者失败回调,再将回调值存储到结果数组中
*/
static all(array) {
let results = []
let index = 0
return new MyPromise ( (resolve, rejecct) => {
function addArrData(i, data) {
results[i] = data
index++
if (index === array.length) {
resolve(array)
}
}
for (let i = 0; i < array.length; i++) {
if (array[i] instanceof MyPromise) {
array[i].then( success => addArrData(i, success), error => rejecct(error))
} else {
addArrData (i, array[i])
}
}
})
}
Promise中的静态方法 -- resolve 方法
代码10:
/**
* 思路:
*
* 1、判断传入的值是否是Promise对象
* 是: 直接将其返回出去
* 否: new一个Promise对象将这个值传递进去,这样就返还一个Promise对象
*/
static resolve (value) {
if (value instanceof MyPromise) return value
else return new MyPromise(resolve => resolve(value))
}
Promise中的内置方法 -- finally (callBack)
代码11:
/**
* 思路:
* 1、根据this.then 可以知道当前Promise 的返回结果,在成功和失败回调中都执行一下fifnally的回调函数
* 2、这里会有一个问题:当对回调函数中有异步代码的时候,return会直接先返回,这里要处理一下这个情况
* 3、可以使用Promise.resolve方法现将finally的回调函数扁平化处理,并且进行异步操作
* 4、再根据返回值进行.then 的操作,进行成功回调和失败回调
*/
finally (callBack) {
this.then ( value => {
// callBack()
// return value
return new MyPromise.resolve(callBack).then( () => value )
}, reason => {
// callBack()
// throw Error(reason)
return new MyPromise.resolve(callBack).then( () => { throw reason} )
})
}
Promise中的内置方法 --catch(failCallBack)
代码12:
/**
* 思路: 根据this.then 的方法返回值,获取执行结果,再直接让失败回调在this.then的失败回调中执行
*/
catch (failCallBack) {
this.then(undefined, failCallBack)
}
4、Promise的结尾
关于Promise的实现基本的已经完成,还有一些的race,reject方法之类的后期会进行补充,通过手写一遍,我更清楚的知道了Promiose的使用和执行方式。
我是荆小乐,每两周都会更新一些我最近所学的知识和理解,希望能和你们共勉,如有不足,请指正,想知道更多内容,就请关注我,更多内容我们下期见!