一、Promise 核心逻辑的实现
在实现其原理之前,必须要对promise有足够多的了解。
分析一波:
- Promise是一个类,在new一个promise实例的时候需要传递一个执行器(函数)进行,执行器会立即执行。
- Promise有两个实例的方法 resolve 方法和 reject 方法,这两个方法会在执行器函数执行的时候作为参数传入,用于改变Promise的状态。
- Promise有三种状态,分别为:成功状态 -> fulfilled ,失败状态 -> rejected, 等待状态 -> pending。Promise默认状态为等待状态,状态的更改通过resolve方法和reject方法,resolve方法用于将状态从pending更改为fulfilled,reject方法用于将状态从pending状态更改为rejected。一旦状态更改就不能再更改了。
- Promise还有一个原型上的then方法,该方法接受两个回调函数,一个是成功状态(fulfilled)的回调函数,一个是失败状态(rejected)的回调函数。两个回调函数分别接受resolve和reject传递的value值。
- then方法还可以进行链式调用,这表明then方法中返回了一个promise对象,后面的then方法可以拿到前面一个then方法的返回值。
1. promise处理同步代码
// 根据上面的分析,我们可以得到如下代码
// 2. 定义Promise三种状态
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
// 1. 执行器会立即执行,接受两个实例方法
constructor(executor) {
executor(this.resolve, this.reject)
}
// 实例属性定义
// promise状态默认为 等待状态
status = PENDING
// 成功状态的值
value = null
// 失败状态的值
reason = null
// 必须要用箭头函数,来将其this与实例进行绑定
resolve = (value) => {
// 3. resolve方法将promise状态便成为成功的状态,状态变更之后就不能够再次更改.
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
}
reject = (reason) => {
// 4. reject方法将promise状态便成为失败的状态,状态变更之后就不能够再次更改.
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
}
// 定义原型then方法
then(successCallback, faildCallback) {
// 5. 判断promise的状态,如果是成功的状态调用成功的回调函数,如果是失败的状态调用失败的回调函数
if (this.status === FULFILLED) {
// 回调函数的值,需要在调用resolve方法的时候进行保存
successCallback(this.value)
} else if (this.status === REJECTED) {
// 回调函数的值,需要在调用rejected方法的时候进行保存
faildCallback(this.reason)
}
}
}
// 测试一波
const promise = new MyPromise((resolve, reject) => {
// resolve('hello reborn jiang')
// reject('hello reborn jiang')
setTimeout(() => {
resolve('hello reborn jiang')
}, 0);
})
promise.then(value => {
console.log(value, '成功状态的值') // hello reborn jiang 成功状态的值
}, reason => {
console.log(reason, '失败状态的值') // hello reborn jiang 失败状态的值
})
为什么当执行器(executor)函数中有异步代码的时候会导致没有任何打印消息? 解释: 当执行器函数里面拥有异步代码的时候,此时调用then方法的时候回调函数就不再执行。导致在执行then方法的时候此时还没有调用过resolve方法与reject方法去更改promise的状态,所以状态还依旧是pending状态,可是then方法中还没有对pending状态进行处理。所以就没有打印结果。
从以上分析中可以得到一点结论: then方法中如果此时时pending状态,执行器中的代码一定时异步的任务
上面解释涉及到js执行机制,简单来说 这是因为执行器里面的是异步代码,异步任务会交给浏览器的的线程来处理,等待异步任务完成时将其加入消息队列中,等待调用栈代码执行完之后EventLoop会从消息队列取任务执行。 如果对JS执行机制还不是很了解的朋友们,可以看笔者的另外一篇关于JS执行机制的文章哈~
2. promise 处理异步代码
分析一波:
- then方法中如果此时还是pending状态,证明执行器中的代码一定时异步的任务。
- 如果在pending状态下直接调用then方法的两个回调函数的话是拿不到resolve或是reject传递的值的。
- 所以我们将在pending状态的时候将回调函数存储起来,等到resolve方法或是reject方法执行的时候在调用then方法的成功或是失败的回调。
// 基于以上分析我们可以得到如下代码
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
// 2. 定义两个变量存储成功或是失败的回调
successCallback = null
faildCallback = null
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
// 3. 在resolve方法执行时再次调用。
this.successCallback && this.successCallback(value)
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
// 3. 在resolve方法执行时再次调用
this.faildCallback && this.faildCallback(reason)
}
then(successCallback, faildCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
faildCallback(this.reason)
} else {
// pengding 证明executor有异步任务
// 1. 存储回调函数,待resolve,reject执行时再次调用
console.log('哦时异步任务,pending中的代码执行了')
this.successCallback = successCallback
this.faildCallback = faildCallback
}
}
}
// 测试一波
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('hello reborn jiang')
}, 0);
})
promise.then(value => {
console.log(value, '成功状态的值')
}, reason => {
console.log(reason, '失败状态的值')
})
// 打印结果如下:
// 哦时异步任务,pending中的代码执行了
// hello reborn jiang 成功状态的值
总结: 哈哈哈,你真棒~ 到这里你已经实现了一个简版的promise可以用来处理同步和异步任务了,接下来挑战难度升级,我们将实现then方法的相关功能与一些细节。
二、then方法功能进一步的实现
1. promise实例的then方法可以多次调用
promise实例的then方法在处理异步任务可以多次调用,其所调用的每个then方法中的回调函数都会执行。
Promise(示例):
//
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello reborn jiang')
},0)
})
promise.then(res => {
console.log(res, '第一次调用')
})
promise.then(res => {
console.log(res, '第二次调用')
})
promise.then(res => {
console.log(res, '第三次调用')
})
// console结果如下:
// hello reborn jiang 第一次调用
// hello reborn jiang 第二次调用
// hello reborn jiang 第三次调用
MyPromise(示例):
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('hello world')
}, 0);
})
promise.then((value) => {
console.log(value, '成功状态的值1')
})
promise.then((value) => {
console.log(value, '成功状态的值2')
})
promise.then((value) => {
console.log(value, '成功状态的值3')
})
// 打印如下:
// 哦时异步任务,pending中的代码执行了
// 哦时异步任务,pending中的代码执行了
// 哦时异步任务,pending中的代码执行了
// hello world 成功状态的值3
结论如下: 出现这种情况的原因是因为在处理异步任务的时候,then方法会先执行,在then方法的pending条件中存储回调的方法仅仅只是一个变量,这也就导致不能够都所有=的回调函数保存,因此最后一个执行的then会覆盖前面的回调函数。
MyPromise(修改版):
// 基于以上分析我们可以得到如下代码
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
// successCallback = null
// faildCallback = null
// 1. 存储回调函数的变量需要是一个Array,确保每个回调都能够存储。
successCallbackList = []
faildCallbackList = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
// this.successCallback && this.successCallback(value)
// 3. 等待异步任务回调执行的时候,开始循环遍历then回调函数执行。
while (this.successCallbackList.length)this.successCallbackList.shift()(value)
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
// this.faildCallback && this.faildCallback(reason)
// 3. 等待异步任务回调执行的时候,开始循环遍历then回调函数执行。
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
faildCallback(this.reason)
} else {
// this.successCallback = successCallback
// this.faildCallback = faildCallback
// 2. 将每次调用的回调函数存储到数组中
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
}
}
// 测试一波
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('hello reborn jiang')
}, 0)
})
promise.then((value) => {
console.log(value, '成功状态的值1')
})
promise.then((value) => {
console.log(value, '成功状态的值2')
})
promise.then((value) => {
console.log(value, '成功状态的值3')
})
// 打印结果
// hello reborn jiang 成功状态的值1
// hello reborn jiang 成功状态的值2
// hello reborn jiang 成功状态的值3
2. then 方法的链式调用
分析一波:
- then方法可以进行链式调用,证明其内部有返回新的promise对象
- then方法回调函数返回值分为两种情况,一种是返回普通值,直接调用新创建的promise对象的resolve将值给保存在这个新创建的实例上。如果是then方法返回的是promise对象,我们需要了解到这个promise的状态是成功的还是失败的,如果是成功的调用resolve方法,如果是失败的调用reject方法。(补充说明普通值的情况)
- 普通值:前面一个then方法的回调函数(无论成功回调或是失败回调)的返回值会作为后面一个then方法的成功回调的参数
Promise代码如下(示例)
// 前面两点总结示例代码
const promise = new Promise((resolve, reject) => {
resolve('hello reborn')
})
promise.then(value => {
console.log(value, '第一个then')
return 'hello David'
}).then(value => {
console.log(value, '第二个then')
}, )
// 打印如下
// hello reborn 第一个then
// hello David 第二个then
// 第二点的解释 失败回调的案例
const promise = new Promise((resolve, reject) => {
reject('hello reborn')
})
promise.then(() => {},
reason => {
console.log(reason, '失败回调')
return 'errorr'
}).then(value => {
console.log(value, '成功回调')
}, reason => {
console.log(reason, '失败回到')
})
// 打印如下
// hello reborn 失败回调
// errorr 成功回调
// then回调返回promise对象的情况
const promise = new Promise((resolve, reject) => {
resolve('hello reborn')
})
promise.then(value => {
console.log(value, '第一个then执行')
return new Promise((resolve, reject) => {
resolve('success to SecondThen')
})
}).then(value => {
console.log(value, '第二个then方法执行')
})
// 打印结果
// hello reborn 第一个then执行
// success to SecondThen 第二个then方法执行
MyPromise代码:
// 基于以上分析我们可以得到如下代码
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()(value)
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
// 1. 支持链式调用,肯定需要返回一个新的promise(不能够返回this,是因为旧的promise可能包含了状态值,回调函数list等等)
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 2. 需要判断当前promise对象返回的是普通值还是promise对象
// 普通值直接调用resolve方法将值传递给到下一个then方法
// 如果是then方法返回的是promise对象,需要判断当前返回的promise对象的状态
// 如果是成功的状态,调用promise2的resolve方法将then返回的promise对象的值传递给到下一个then
// 如果是失败的状态,调用promise2的reject的方法将then返回的promise对象的失败值传到下一个then的第二个参数
const successCallBackValue = successCallback(this.value)
// 3. 不仅要在FULFILLED情况下要判断,REJECTED, PENDING都要判断,封装一个方法
resolvePromise(successCallBackValue, resolve, reject)
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
}
function resolvePromise(returnVal, resolve, reject) {
// 4. 判断状态
if (returnVal instanceof MyPromise) {
// 6. promise对象, 查看状态之后,调用resolve 或是 reject
// returnVal.then(value => resolve(value), reason => reject(reason))
// 可以简写如下
returnVal.then(resolve, reject)
} else {
// 5. 是普通值,调用resolve方法将当前then的返回值传递给到下一个then
resolve(returnVal)
}
}
// 测试一波
const promise = new MyPromise((resolve, reject) => {
resolve('hello reborn jiang')
})
// 普通值
promise
.then((value) => {
console.log(value, '第一个then')
return 'hello world'
}).then(value => {
console.log(value, '第二个then')
})
// 输出结果如下
// hello reborn jiang 第一个then
// hello reborn jiang 第二个then
// promise对象
promise.then(value => {
console.log(value, '第一个then')
return new MyPromise((resolve, reject) => {
resolve('success to secondThen')
})
}).then(value => {
console.log(value, '第二个then')
})
// 输入如下:
// hello reborn jiang 第一个then
// success to secondThen 第二个then
// 测试第一个then返回的是带有失败状态的promise
promise.then(value => {
console.log(value, '第一个then')
return new MyPromise((resolve, reject) => {
reject('faild to secondThen')
})
}).then(value => {
console.log(value, '第二个then成功')
}, reason => {
console.log(reason, '第二个then 失败')
})
// 打印结果如下:
// hello reborn jiang 第一个then
// faild to secondThen 第二个then 失败
恭喜你,你已经实现了大部分then方法的功能,还剩下一些小细节待完善,继续加油鸭~
后面的内容会在今天完成,请持续关注哦~
3. then方法的链式调用识别 Promise 对象自返回
解释一下标题的意思: 在then方法的链式调用中,如果当前then方法返回的promise对象又作为当前then方法回调函数的返回值这会发生什么问题,大家可以先思考一下?再继续往下阅读~
Promise代码如下(示例)
// promise then返回当前对象
const promise = new Promise((resolve, reject) => {
resolve('hello morning')
})
const promise1 = promise.then(res => {
return promise1
})
// 打印如下:
// TypeError: Chaining cycle detected for promise #<Promise>
// 抛出类型错误,检测到 promise 链式循环
用我们之前的代码演示 then方法的链式调用时 Promise 对象自返回,会带来什么样的问题?
MyPromise 代码如下(示例)
要在then回调中拿到当前then返回的promise对象有两种方法 第一种方法
const promise = new MyPromise((resolve, reject) => {
resolve('hello reborn')
})
// 1. 此时我们的 MyPromise 还不能够想 Promise 一样, 在then方法里直接拿到 当前then方法返回的promise对象,这是因为 then方法里还没有做任何处理。
// const promise1 = promise.then(value => {
// return promise1
// })
// 2. 不过没关系,我们可以借助定时器,等then方法内执行回调函数时发现又定时器,就会继续往下执行,先返回then方法的promise1对象之后回来再执行回调函数里面的内容,此时回调中就能够拿得到promise1了
const promise1 = promise.then(value => {
setTimeout(() => {
return promise1
}, 0);
}).then(res => {
console.log(res, 'promise1') // undefined promise1的返回值
})
// 3. 第二个then 方法并不能够拿得到前面一个then返回的promise对象,拿到的时undefined。
分析
第二种方法
then(sucessCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 1. 利用定时器延期执行回调函数。原理与文章第一种方法相同
setTimeout(() => {
const sucessCallBackValue = sucessCallback(this.value)
resolvePromise(sucessCallBackValue, resolve, reject)
}, 0);
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
console.log('是否执行了两次')
sucessCallback instanceof Function &&
this.sucessCallbackList.push(sucessCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
const promise = new MyPromise((resolve, reject) => {
resolve('hello reborn')
})
const promise1 = promise.then(value => {
return promise1
}).then(res => {
console.log(res, 'promise1')
})
// 打印结果
// 是否执行了两次
// MyPromise {
// status: 'pending',
// value: null,
// reason: null,
// sucessCallbackList: [],
// faildCallbackList: [],
// resolve: [Function: resolve],
// reject: [Function: reject]
// } returnVal
// 是否执行了两次
代码执行结果分析如下:
结论
如果不对 当前then方法返回的promise对象又作为当前then方法的回调的返回值这种情况做处理,会导致调用链执行 ”断层“ 的这种情况,即除了第一个确定状态的promise的then方法的回调能够执行,其他then方法返回的promise对象都处于pending状态,然后其回调函数都会被推到successCallBackList或是faildCallBackList中等待当前promise(也就是第一个then方法)中的resolve方法或是reject方法执行之后下一个then方法的才会执行其回调函数。问题是第一个then方法的回调中,将本来用于调用第二个then方法回调的resolve和reject方法push到successCallBackList或faildCallBackList中,导致后面的then方法的回调都没有办法执行,在第一个then回调执行直接也没有能够调用resolve或是reject,自然后面一个then回调不能够执行,因为后面一个then回到不能够执行,它里面的resolve reject也不能够调用,第三个也不能执行........后面的所有then回调都不能执行。
看下面的图更容易理解
避免自调用的问题很简单,请查看下面代码
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()(value)
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
const successCallBackValue = successCallback(this.value)
// 1. 将 then方法中返回的promise2传入给到resolvePromise方法中 去跟 then回调返回的值做判断。
resolvePromise(promise2, successCallBackValue, resolve, reject)
}, 0);
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
// 2. 如果相等之后抛出异常并阻止代码向下执行
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) => {
resolve('hello reborn')
})
const promise1 = promise.then(res => {
console.log(res)
return promise1
})
promise1.then(() => {}, reason => {
console.log(reason.message)
})
// 打印结果如下:
// hello reborn
// Chaining cycle detected for promise #<Promise>
4. then方法捕获异常处理,完善其他功能
任何一个工具库或是框架都会进行异常处理,来保持代码的"健壮性,现在来完善MyPromise" 分析:
- 我们首先要先确保自己的代码不会报错,那么错误的来源只有可能是外部输入性的错误,也只有外部回调函数会参与我们的代码执行
- executor执行函数
- then方法的回调函数调用
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// 1. 利用try catch 来捕获代码错误
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()(value)
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) => {
resolve('hello reborn')
// 2. 打印一个未定义的a
// console.log(a)
})
// 3. 测试一波
promise.then(res => {
}, reason => {
// executor错误
console.log(reason.message, '打印错误')
})
// 打印结果
// a is not defined 打印错误
// 回调错误
promise.then(res => {
// 4. 在then方法创建一个回调的错误,在下一个then的失败的回调函数需要进行捕获到
// throw new Error("")
console.log(b)
}).then(res => { }, reason => {
console.log(reason.message, '捕获then方法的错误')
})
// 打印结果
// b is not defined 捕获then方法的错误
此时我们在then方法也只对状态为FULFILLED情况处理成功,REJECTED和PENDING都没有进行处理,请看下面对REJECTED与PENDING代码做的处理
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()()
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()()
}
then(successCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else if (this.status === REJECTED) {
// 1. 对失败状态的处理与成功是一样的
// - 对失败的的回调函数做异常处理
// - 将失败函数的普通返回值作为下一个then方法成功函数的返回值
// - 如果返回的是promise对象,调用当前promise对象的then方法取得返回值,通过resolve或是reject方法传递给到下一个then回调中
// - promise对象自调用需要抛出异常
setTimeout(() => {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else {
// 2. 对pending状态的代码做处理
// - 对then方法的回调函数做异常处理,此时我们需要外部再用一个函数进行包裹,里面调用成功回调才能写try catch
// - 剩下与 FULFILLED && REJECTED 状态时的处理一样的
successCallback instanceof Function &&
this.successCallbackList.push(() => {
// 感觉这个定时器可有可无,then方法会先执行返回promise对象,回调函数会在thne方法之后执行,这里肯定是可以拿得到then方法返回的promise对象的
setTimeout(() => {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
})
faildCallback instanceof Function &&
this.faildCallbackList.push(() => {
setTimeout(() => {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
})
}
})
return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) => {
reject('no reborn')
})
// 3. 测试then方法对失败函数的处理
promise.then(res => {
}, reason => {
console.log(reason)
return '传递给到下一个then'
}).then(res => {
console.log(res)
})
// 打印结果:
// no reborn
// 传递给到下一个then
// 4. 测试pending状态下的处理
promise.then(res => {
console.log(res)
return 'hello Boom'
}).then(res => {
console.log(res)
})
// 打印结果
// hello reborn
// hello Boom
5. 将then变为可选参数
then方法可以不传递回调作为参数,会将promise的状态一直向下传递,直到then方法有回调函数的时候接受传递的参数
Promise演示
const promise = new Promise((resolve, reject) => {
resolve('hhhhh')
})
promise.then().then().then().then(res => {
console.log(res)
})
// 打印结果
// hhhhh
分析:
- 判断then方法有没有回调函数,如果没有回调函数的话,我们就补一个回调函数 value => value,这样就会将状态进行传递下去。
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()()
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()()
}
then(successCallback, faildCallback) {
// 1. value => value 可以将上一个promise对象的值 给到当前then返回的promise对象,下个then就可以接受到
successCallback = successCallback instanceof Function ? successCallback : value => value
// 2. 如果时失败的状态的promise可以利用 reason => { throw reason} ,这样回调函数抛异常被catch捕获到当前then方法就是失败的状态promise,下个then就可以直接获取
faildCallback = faildCallback instanceof Function ? faildCallback : reason => {throw reason}
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
// 3. 让当前then为REJECTED
reject(err)
}
}, 0);
} else {
this.successCallbackList.push(() => {
setTimeout(() => {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
})
this.faildCallbackList.push(() => {
setTimeout(() => {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
})
}
})
return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('hello reborn')
}, 0);
})
promise.then().then().then(res => {
console.log(res)
})
// 打印结果
// hello reborn
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('no hello reborn')
}, 0);
})
promise.then().then().then(res => {
console.log(res)
}, reason => console.log(reason))
// 打印结果
// no hello reborn
三、Promise对象静态方法实现
1. Promise.all
Promise.all 用来解决异步并发问题,接下来我们将分析分析他的特性都有那些 分析
- promise.all 时promise的静态方法,需要通过static关键字来定义
- promise接受一个数组作为参数,数组中可以是普通值或是promise对象
- promise.all 方法返回一个promise对象,至于返回的这个promise对象是成功的,还是失败的取决于数组中所有值的结果。 如果所有的值都是成功的,返回的就是成功的状态,如果有一个是失败的,返回的就是失败的状态。如果promise对象是成功的状态,可以拿得到数组中值的处理结果,结果是按照数组的顺序来的。
- 数组中值如果是普通值直接直接push到结果集(数组)中,如果是promise对象拿到其promise对象的值push到结果集(数组)中。
// 只展示关键代码
// 根据我们上诉的分析我们可以得到以下代码
// 1. 定义静态方法
static all(array) {
// 3. 定义一个数组用来装数组中值的处理结果
let results = []
// 2. 返回一个promise对象
return new MyPromise((resolve, reject) => {
// 4. 我们需要用for循环对每一个值都进行处理
for(let i = 0; i < array.length; i++) {
// 5. 数组的值有可能是普通数据,也有可能是promise对象,对传进来的值判断。
if (array[i] instanceof MyPromise) {
// 7. 如果是promise对象,我们需要通过then方法拿到其结果
array[i].then(value => results.push(value), reason => reject(reason))
} else {
// 6. 对传入的参数不是 promise 对象直接push到数组中
results.push(array[i])
}
}
// 8. for循环结束之后需要更改promise对象的状态
resolve(results)
})
}
// 测试一波
function p1() {
return new MyPromise((resolve, reject) => {
resolve('hello')
})
}
function p2() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('reborn jiang')
}, 0)
})
}
MyPromise.all([p1(), p2(), 1,2,3]).then(value => {
console.log(value, 'promise.all返回的结果')
})
// 打印结果
// [ 1, 2, 3, 'hello' ] promise.all返回的结果
此时你应该发现这个返回的结果有些问题啊
- all方法返回的promise对象拿到的结果集的顺序是不对的。
- 传递给all方法的是5个参数,最后拿到的结果集确实少了一个是异步promise对象p2的结果。 可以先思考下为什么会出现这样的问题。 第二个点我还没有想明白,容我想想再更新文章。
2. Promise.resolve
Promise.resolve方法目的是快速创建一个promise对象 分析
- Promise.resolve方法是一个静态的方法
- 他接受一个参数,参数可以是普通值或promise对象。
- 如果接受是普通值,他就返回一个promise对象,并将这个值作为promise的resolve方法的参数
- 如果是promise对象就返回这个promise对象
// 仅展示关键代码
// 根据上面分析我们可以得到如下代码
static resolve(value) {
// 1. 如果是promise对象,就返回promise对象
if (value instanceof MyPromise) return value
// 2. 普通值的话我们需要创建一个promise对象,将其值最为resolve参数
return new MyPromise(resolve => resolve(value))
}
// 测试一波
function p1() {
return new MyPromise((resolve, reject) => {
resolve('hello')
})
}
// 3. 传入普通值
MyPromise.resolve('rrrrbbbb').then(val => {
console.log(val)
})
// 4. 传入promise对象
MyPromise.resolve(p1()).then(value => {
console.log(value)
})
// 打印结果如下:
// rrrrbbbb
// hello
3. Promise.reject
与promise.resolve方法同理 Promise.reject 快速创建一个失败的promise对象 分析
- 静态方法
- 传递promise对象会抛出异常 UnhandledPromiseRejectionWarning: hello(为promise的resolve,reject接受的值)
- 普通值返回promise对象并将参数作为reject方法入参 代码很简单,可以自己试试哈~
四、finally方法的实现
finally为在Promise是否成功完成后都需要执行的代码提供了一种方式。 分析:
- promise方法是promise对象的原型方法
- finally() 方法返回一个Promise
- finally()接受一个回调函数作为参数,无论结果是fulfilled或者是rejected,都会执行指定的回调函数
- finally方法callback如果又异步代码,会等到回调函数执行结束之后,finally返回的promise对象的then方法才会执行。
// 根据上面分析我们可以得到如下代码
// 1. 原型方法
finally(callback) {
// 2. 无论是fulfilled 还是 rejected 都会执行回调函数,只有then方法知道promise对象的状态
// 3. 返回一个promise对象,正好then方法就返回promise对象
return this.then(value => {
callback()
return value
}, reason => {
callback()
throw reason
})
}
const promise = new MyPromise((resolve, reject) => {
// resolve('hello reborn')
reject('hello reborn')
})
// 4. callback 是同步代码时候输出符合目标
promise.finally(() => {
console.log('finally执行了')
}).then(res => {
console.log(res)
}, reason => console.log(reason))
// 打印结果
// finally执行了
// hello reborn
// 5. 又异步代码的时候测试结果的时候不符合预期了,
promise.finally(() => {
setTimeout(() => {
console.log('finally执行了')
}, 3000)
}).then(res => {
console.log(res)
}, reason => console.log(reason))
// 打印结果
// hello reborn
// finally执行了
我们只需要让callback里面的代码执行结束之后,在调用resolve或是reject方法就可以达到目标。 可以借助于Promise.resolve方法
// 根据上面分析我们可以得到如下代码
// 1. 原型方法
finally(callback) {
// 2. 无论是fulfilled 还是 rejected 都会执行回调函数,只有then方法知道promise对象的状态
// 3. 返回一个promise对象,正好then方法就返回promise对象
return this.then(value => {
// 4. 通过resolve方法再调用then得到一个promise对象,最后在将这个通过resolve方法再调用then生成的romise对象返回出去,
// 最外层的then方法的返回值是promise对象,然后 回取的resolve方法生成的promise对象的结果,将结果作为自己的promise对象resolve的参数
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
// 5. resolve方法再调用then得到一个失败promise对象,再将这个对象返回出去。然后外层then回调用返回的promise
// then方法取到值,调用reject()将值传递。
return MyPromise.resolve(callback()).then(() => {throw reason})
})
}
五、catch方法的实现
catch方法很简单,实际上就是再是再catch方法中调用then方法,不过then方法的第一个参数不传,只传递失败的回调参数。请看下面代码的实现~
// 仅展示关键代码
catch(faildCallback) {
return this.then(undefined, faildCallback)
}
promise.then(res => { }).catch(res => {
console.log(res)
})
// 打印结果
// hello reborn