前言
本文主要会讲解 PromiseA+ 规范,会实现一个能通过所有 PromiseA+ 规范的测试用例的 Promise,本文不再讲解promise的使用,也会涉及到一些高阶函数和柯里化的使用,需要提前去掌握一下
Promise的状态
- Promise有三个状态,分别是等待态(PENDING)完成态(RESOLVE)拒绝态(REJECTED)状态初始化为等待态,一旦变为完成或拒绝就无法再修改
const PENDING = 'PENDING'
const RESOLVEED = 'RESOLVEED'
const REJECTED = 'REJECTED'
Promise的初始化属性
- 实例化必须传入一个方法
- 初始化状态为 PENDING
- 初始化一个value、reason 后面用于保存完成的值、拒绝的原因
- 初始化一个 resolveCallcacks 的数组,用于保存完成后执行的回调
- 初始化一个 rejectCallcacks 的数组,用于保存拒绝后执行的回调
class Promise {
constructor(executor) {
if(typeof executor !== "function") throw Error('传入的参数必须是一个方法')
this.status = PENDING
this.value = undefined
this.reason = undefined
this.resolveCallbacks = []
this.rejectCallbacks = []
}
}
Promise 构造器里调用传入的执行方法
- executor方法会立即执行,方法会有两个入参,第一个是可以转换成完成态的 handleResolve 方法,另一个则是能转换成拒绝态 handleReject 的方法,两个方法可以传入一个value或者reason的参数,用于表示当前的Promise的值或者是拒绝的原因
- 在执行 executor 方法时报错也会调用 handleReject 进去拒绝态
- 一旦在 executor 在执行 handleResolve 或者 handleReject 后会进行状态修改、保存参数以及执行通过“实例.then”注册的对应的方法
class Promise {
constructor(executor) {
// ......
const handleResolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVEED
this.resolveCallbacks.forEach(cb => cb(this.value))
}
}
const handleReject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTETED
this.rejectCallbacks.forEach(cb => cb(this.reason))
}
}
try {
executor(handleResolve, handleReject)
} catch (e) {
handleReject(e)
}
}
}
then的两个方法 onFulfilled 和 onRejected 的初始化处理
- then方法有两个可选参数是 onFulfilled 和 onRejected 分别对应是完成态的回调和拒绝态的回调,一般情况下这两个是一个方法,方法的第一个参数能取到 Promise的 value 或 reason
- onFulfilled onRejected 两个可选参数如果没传或者是传入一个非方法类型的值,会转成一个方法,前者将Promise的Value向下进行传递,后者会抛出一个错误(其实也是属于向下传递)
class Promise {
constructor(executor) {
// ......
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
}
}
}
Promise 链式操作、回调注册
- Promise 是利用返回了一个新的Promise实现链式调用
- 在执行 onFulfilled 或者 onRejected 会以trycatch进行捕捉错误,如果没有错误则将 onFulfilled的返回值作为 nextResolve进行调用(往下执行),有错误则将捕获的错误作为reason进行调用 nextReject(柯里化封装一个通用的处理回调函数的方法)
- 根据当前的状态,合粒化 RESOLVE 则会执行 onFulfilled 否则则会执行 onRejected 同时传入相应 value 或 reason
- 如果当前状态是等待态的话,会将执行的回调存到 resolveCallbacks 和 rejectCallbacks
- 如果当前是完成态或拒绝态(也就是同步执行的话),那就直接调用 onFulfilled 或 onRejected
class Promise {
constructor(executor) {
// ......
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
}
return new Promise((nextResolve, nextReject) => {
const publicFunc = handleCallback => valueOrReason => {
try{
const x = handleCallback(valueOrReason)
nextResolve(x)
}catch (e) {
nextReject(e)
}
}
switch (this.status) {
case PENDING:
// 这里的 value 或者 reason 是由构造器中 handleResolve handleReject方法调用传入
this.resolveCallbacks.push(publicFunc(onFulfilled))
this.rejectCallbacks.push(publicFunc(onRejected))
break
case RESOLVEED:
publicFunc(onFulfilled)(this.value)
break
case REJECTED:
publicFunc(onRejected)(this.reason)
break
}
})
}
}
处理 onFulfilled 和 onRejected 返回值
- 实现一个 resolvePromise 方法并传入返回的promise,x ,nextResolve,nextReject进行统一处理
- 由于在 new 实例过程中,constructor调用时实例还未返回,所以使用一个 setTimeout 方法进行处理,可以异步获取到 new 之后的实例
class Promise {
constructor(executor) {
// ......
}
then(onFulfilled, onRejected) {
// ......
const promise2 = new Promise((nextResolve, nextReject) => {
const publicFunc = handleCallback => valueOrReason => {
setTimeout(() => {
try {
const x = handleCallback(valueOrReason)
resolvePromise(promise2, x, nextResolve, nextReject)
} catch (e) {
nextReject(e)
}
}, 0)
}
switch (this.status) {
//......
}
})
return promise2
}
}
- 由于返回值的 x 可能也是一个 Promise 实例,并且如果返回的实例跟返回值 x 是同一个实例,就会出现循环等待,在 Promise 规范里会以将这个类型错误作为返回 nextReject 的值
- 在 Promise 规范中如果当前是一个对象(包含数组和方法)并且有 then 方法,则会认为这是一个 Promise 实例,就会直接调用 then 方法,并且如果 then 后还是一个promise,就会实现一个递归调用,直到找到 resolve 的值,或者是 reject 的拒因
- 并且为了防止会出现重复调用 nextResolve 和 nextReject ,使用一个 called 变量
const resolvePromise = (promise, x, resolve, reject) => {
if (promise === x) return reject(new TypeError('当前会引起循环等待'))
let called;
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === "function") {
// promise
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
使用 promises-aplus-tests 运行测试用例对实现的 Promise进行测试
// 第一步: 下载
npm i promises-aplus-tests -g
// 第二步在 promise.js 下方添加下面的代码
class Promise{
// ......
}
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
// 第三步在命令行中 后面是你当前的文件路径
promises-aplus-tests ./promise.js
完整代码
const PENDING = 'PENDING'
const RESOLVEED = 'RESOLVEED'
const REJECTED = 'REJECTED'
const resolvePromise = (promise2, x, nextResolve, nextReject) => {
if (promise2 === x) return nextReject(new TypeError('当前会引起循环等待'))
let called;
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === "function") {
// promise
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, nextResolve, nextReject)
}, err => {
if (called) return
called = true
nextReject(err)
})
} else {
nextResolve(x)
}
} catch (e) {
if (called) return
called = true
nextReject(e)
}
} else {
nextResolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.resolveCallbacks = []
this.rejectCallbacks = []
if (typeof executor !== "function") throw Error('出错')
const handleResolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVEED
this.resolveCallbacks.forEach(cb => cb(this.value))
}
}
const handleReject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.rejectCallbacks.forEach(cb => cb(this.reason))
}
}
try {
executor(handleResolve, handleReject)
} catch (e) {
handleReject(e)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
}
const promise2 = new Promise((nextResolve, nextReject) => {
const publicFunc = handleCallback => valueOrReason => {
setTimeout(() => {
try {
const x = handleCallback(valueOrReason)
resolvePromise(promise2, x, nextResolve, nextReject)
} catch (e) {
nextReject(e)
}
}, 0)
}
switch (this.status) {
case PENDING:
this.resolveCallbacks.push(publicFunc(onFulfilled))
this.rejectCallbacks.push(publicFunc(onRejected))
break
case RESOLVEED:
publicFunc(onFulfilled)(this.value)
break
case REJECTED:
publicFunc(onRejected)(this.reason)
break
}
})
return promise2
}
}
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise