目录
Promise构造函数,Promise.prototype.then方法重写
(5条消息) 随笔-对Promise的深入理解_qfc_128220的博客-CSDN博客
前面完成了Promise构造函数和Promise.prototype.then方法的重写
但是还有如下方法没有重写
Promise.prototype.catch
Promise.prototype.finally
Promise.resolve
Promise.reject
Promise.all
Promise.race
这里我们先用es6 class语法将之前重写Promise构造函数和Promise.prototype.then再次重写一下
Promise构造函数,Promise.prototype.then方法重写
class Promise{
constructor(excutor) {
this.promiseState = 'pending'
this.promiseResult = null
// 异步回调需要被缓存到调用then的promise对象中,等待promise对象状态改变后执行,由于同一个promise对象可以分别调用多次then,而多次then指定的都是异步回调,所以需要缓存它们
this.callbacks = []
// 这里写成箭头函数,就不需要改变函数this指向了
const resolve = (data) => {
if(this.promiseState !== 'pending') return
this.promiseState = 'fulfilled'
this.promiseResult = data
if(this.callbacks.length !== 0) {
this.callbacks.forEach(item => {
item.onResolved(data)
})
}
}
const reject = (err) => {
if(this.promiseState !== 'pending') return
this.promiseState = 'rejected'
this.promiseResult = err
if(this.callbacks.length !== 0) {
this.callbacks.forEach(item => {
item.onRejected(err)
})
}
}
try {
excutor(resolve, reject)
} catch(e) {
reject(e)
}
}
then(onResolved, onRejected){
// then方法可以不指定成功回调,此时需要生成默认成功回调,负责数据传递
if(typeof onResolved !== 'function') {
onResolved = data => data
}
// then方法可以不指定失败回调,此时需要生成默认失败回调,负责异常穿透
if(typeof onRejected !== 'function') {
onRejected = err => { throw err }
}
// then方法的返回值是一个新的promise对象
return new Promise((resolve, reject) => {
// 提取设置then方法返回的新promise对象的状态和结果的方法
function common(onType, onTypeParam){
try {
const result = onType(onTypeParam)
// 如果then指定回调函数的返回值是promise对象,则then方法返回值promise对象的结果和状态与 then方法指定回调函数返回值promise对象的结果和状态一致
if(result instanceof Promise) {
result.then(data => {
resolve(data)
}, err => {
reject(err)
})
} else {
// 如果then指定回调函数的返回值是非promise对象,则then方法返回值promise对象状态一定为成功,且结果为then指定回调函数的返回值
resolve(result)
}
} catch(e) {
// 如果then指定回调函数执行过程中抛出异常,则then方法返回值promise对象为失败,且结果为异常信息
reject(e)
}
}
// 注意下面的this都是then方法调用者promise对象的this,而不是then方法返回值promise对象的this
// 调用then的promise对象的状态在调用then之前就已经确定为非pending,则为同步回调,对应的回调函数可以立即推入异步微任务队列(这里无法实现推入异步微任务队列,所以直接回调函数执行执行)
if(this.promiseState === 'fulfilled') {
common(onResolved, this.promiseResult)
} else if (this.promiseState === 'rejected') {
common(onRejected, this.promiseResult)
} else {
// 用then的promise对象的状态在调用then之后外还是pending的,则会异步回调,此时指定的回调函数需要被缓存在调用then的promise对象中,等待promise对象状态改变后再调用
this.callbacks.push({
// 由于此时指定回调是异步调用的,所以对then方法返回值promise对象的状态和结果设置也需要在异步中进行,而不同在同步代码中设置,所以只能将缓存异步回调和调用异步回调封装在一起(放到一个函数中)
onResolved: function(data){
common(onResolved, data)
},
onRejected: function(err) {
common(onRejected, err)
}
})
}
})
}
}
Promise.prototype.catch方法重写
class Promise {
constructor(excutor){ //... }
then(onResolved, onRejected){ //... }
catch(onRejected) {
return this.then(null, onRejected)
}
}
catch方法是对then方法功能的一种分担,我们直到then方法既可以指定成功回调,也可以指定失败回调。如果调用then的promise对象是失败状态,则会执行then指定的失败回调,如果调用then的promise对象是成功状态,则会执行then指定的成功回调。
但是如果在then链式调用中,我们为每一个then都指定成功回调和失败回调,则显得非常臃肿。为了优化链式调用中then的书写方式,Promise做了两件事:
1、then的默认失败回调,即如果then方法没有指定失败回调,则then方法内部会产生一个默认的失败回调,它的实现是 onRejected = err => { throw err }, 这个默认失败回调的作用是:如果调用then的promise对象是一个失败态的,且then没有指定失败回调,则最终then方法返回一个新的失败态的promise对象,且该promise对象的失败原因和调用者promise对象的失败原因相同。如果then链式中都没有指定失败回调,则会产生一个现象:异常穿透,即第一个then返回的是一个和调用者状态相同(失败态),结果相同的新promise对象,第二个then返回的是和第一个then返回promise对象状态相同,结果相同的新promise对象,这样就能将失败状态和失败原因一致传递到then链式的最后一个then的返回值promise对象
2、catch函数捕获异常,上面then使用默认失败回调会导致异常穿透现象,即then链式最后一个then返回的是一个失败态的,失败原因和初始then调用者promise对象相同的新的promise对象,此时如果不对链式返回的失败态promise对象进行失败回调的话,原生Promise则会抛出异常,所以Promise提供了catch方法来处理失败态的promise对象,而catch函数只接受一个失败回调作为参数,catch方法一般放在then链式尾部来捕获失败原因。
Promise.prototype.finally方法重写
class Promise {
constructor(excutor) { //... }
then(onResolved, onRejected) { //... }
catch(onRejected) { //... }
finally(onFinally) {
return this.then(onFinally, onFinally)
}
}
如果将then类比为try,catch类比catch,finally类比为finally,则会发现,它们其实是功能对应的。
我们只在then中指定成功回调,在catch中指定失败回调,在finally中指定收尾处理。
比如then中需要查询数据库数据,但是查询异常了,被catch捕获到异常处理了,如果只做到这里,则会造成内存泄漏,因为我们必须要将建立的数据库连接释放,在没有finally时,我们需要对catch返回的promise对象,再做一次then(onFinally, onFinally),即无论成功还是失败都指向一样的回调,但是这样书写不好看,所以将其封装为finally方法
Promise.resolve函数重写
class Promise {
constructor(excutor) { //... }
then(onResolved, onRejected) { //... }
catch(onRejected) { //... }
finally(onFinally) { //... }
static resolve(data) {
return value instanceof Promise ? value : new Promise(resolve => resolve(value))
}
}
resolve是Promise类的静态方法,可以快速地创建一个新的promise对象,而不需要繁琐地new Promise
另外resolve返回的promise对象的状态取决于入参,如果入参是非Promise类型数据,则resolve返回值一定是成功态的,且结果就是入参数据,如果入参是Promise类型数据,则resolve直接将入参作为返回值返回
Promise.reject函数重写
class Promise {
constructor(excutor) { //... }
then(onResolved, onRejected) { //... }
catch(onRejected) { //... }
finally(onFinally) { //... }
static resolve(data) { //... }
static reject(data) {
return new Promise((resolve, reject) => {
reject(data)
})
}
}
Promise.reject也是Promise类的静态方法,也是快速返回一个新的promise对象,但是和Promise.resolve不同的是,Promise.reject不管入参是否为Promise类型还是非Promise类型,Promise.reject的返回值一定是一个失败态的promise,且结果就是入参。需要注意的是,即使入参是一个promise对象,也会被封装进Promise.reject返回值promise对象的结果中。
Promise.all函数重写
class Promise {
constructor(excutor) { //... }
then(onResolved, onRejected) { //... }
catch(onRejected) { //... }
finally(onFinally) { //... }
static resolve(data) { //... }
static reject(data) { //... }
static all(arr) {
return new Promise((resolve, reject) => {
let results = []
let count = arr.length
arr.forEach(item => {
item.then(data => {
results.push(data)
if(--count === 0) {
resolve(results)
}
}).catch(err => {
reject(err)
})
})
})
}
}
Promise.all应用场景:我们需要等待多个异步任务的结果,只有所有异步任务的结果都到齐了才能继续后续处理。如果多个异步任务中有一个失败,则整体失败。
Promise.all接收一个数组,数组元素全部为promise对象。Promise.all返回一个新的promise对象,该promise对象的状态和结果取决于Promise.all入参的数组的每一个元素
如果入参数组的所有元素都是成功态的promise,则Promise.all返回的promise对象就是成功态的,且其结果为一个数组,数组元素是Promise.all入参数组的每一个promise对象元素的结果
如果入参数组的某一个元素是失败态的promise,则Promise.all返回的promise对象就是失败态的,且其结果为入参数组失败态promise对象的结果
这里Promise.all的重写需要考虑异步,即如果入参数组元素promise对象的封装的是异步任务,则Promise.all实现需要考虑异步
Promise.race函数重写
class Promise {
constructor(excutor) { //... }
then(onResolved, onRejected) { //... }
catch(onRejected) { //... }
finally(onFinally) { //... }
static resolve(data) { //... }
static reject(data) { //... }
static all(arr) { //... }
static race(arr) {
return new Promise((resolve, reject) => {
arr.forEach(item => {
item.then(data => {
resolve(data)
}).catch(err => {
reject(err)
})
})
})
}
}
Promise.race应用场景:开启多个异步任务做同一件事,谁先完成,就使用谁的结果。如果最快的那么是失败的,则整体失败。
Promise.race也是Promise类的静态方法,接收一个数组,数组元素都是promise对象。
Promise.race返回值是一个新的promise对象,它的状态和结果取决于Promise.race入参的数组中一个完成状态改变的promise对象元素