目录
1.实例方法catch
2.finally方法
3.MyPromise.resolve()
4.MyPromise.reject()
5.MyPromise.race()
6.MyPromise.all()
7.MyPromise.allSettled()
8.MyPromise.any()
总结
在promise中,每一个promise实例都有一个catch方法,该方法接收一个回调函数,用于当promise的状态是rejected时执行对应的逻辑,同时它也可以获取到构造函数中的错误,并通过error参数注入到回调函数中。这里参考了MDN中promise的catch方法的解释,直接调用then方法,并在第二个参数中传入对应的处理逻辑即可。
catch方法的解释: Promise.prototype.catch() - JavaScript | MDN (mozilla.org)
代码如下:
catch(onRejected) {
return this.then(null, onRejected)
}
构造函数中需要捕获错误;
try {
callback(resolve, reject)
} catch(err) {
reject(err)
}
promise实例的finally方法无论promise的状态是成功还是失败,都会执行的一个方法,其需要传入一个回调函数,回调函数不需要出入任何参。这里参考MDN对promise的finally的描述,其相当于调用了then方法,同时传入的两个回调完全一样,这样无论是那种状态,都会执行里面的逻辑。同时finally和then方法一样,也返回一个promise实例。
finally的描述:Promise.prototype.finally() - JavaScript | MDN (mozilla.org)
代码如下:
finally(onFinally) {
return this.then(onFinally, onFinally)
}
原生Promise类的静态方法resolve可以接受一个基础值,内部将其会转为一个成功的promise实例并返回,但需要注意的是,如果传入值是一个promise实例,则会将其直接返回,不做任何处理。(此处参考MDN文档)
Promise.resolve解释:Promise.resolve() - JavaScript | MDN (mozilla.org)
代码如下;
static resolve(value) {
// 判断传入的值是否为promise实例
if(value instanceof MyPromise) {
return value
} else {
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
}
此方法与MyPromise.revolve()显相似,唯一不同的是这个方法不需要判断是否为promise实例,而是无论传入的是什么,直接返回一个拒绝的promise实例。
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value)
})
}
Promise.race()方法用于处理多个异步任务。其接收一个数组作为参数,数组每一项可以是promise实例,也可以时普通值。如果传入的不是一个数组,则会报错,错误信息是:Argument is not iterable。传入的每一项如果是promise实例,则会等待第一个promise状态敲定,然后返回一个与第一个敲定的promise实例状态相同的promise实例。如果数组中某些项不是promise实例,则会其包装成一个成功的promise实例,并在其敲定是返回一个成功的promise实例。
参考MDN:Promise.race() - JavaScript | MDN (mozilla.org)
代码如下:
static race(arr) {
return new MyPromise((resolve, reject) => {
// 判断是否传入数组参数
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 等待第一个promise执行完毕
for(let item of arr) {
MyPromise.resolve(item).then((res) => {
resolve(res)
},err => {
reject(err)
})
}
})
}
Promise.all()方法接收一个promise数组,返回一个promise实例,当数组中所有的promise实例全部成功之后,将所有的promise的结果存入一个数组中并作为返回的promise的结果,当有任何一个promise拒绝之后,将第一个拒绝的promise的原因作为返回的promise的原因。需要注意的是,这个方法和Promise.race()一样,如果传入的不是数组(本质上是可迭代对象),就会报错。如果传入的是空数组,就会将空数组作为结果直接返回,如果传入的数组中有普通值,则将会被包装promise实例。同时在返回的结果数组中,其结果的顺序需要与传入的promise数组中对应相同。
参考MDN:Promise.all() - JavaScript | MDN (mozilla.org)
代码如下:
static all(arr) {
return new MyPromise((resolve,reject) => {
// 判断是否是数组
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 如果是空数组,则直接返回一个成功状态的promise
if(arr.length === 0) resolve([])
// 定一个数组,用于存储结果
const resArr = []
// 兑现次数
let count = 0
// 遍历数组
arr.forEach((item,index) => {
MyPromise.resolve(item).then((res) => {
// 添加到结果数组中
resArr[index] = res
// 计数器+1
count++
// 判断是否全部执行完毕
if(count === arr.length) {
resolve(resArr)
}
},err => {
reject(err)
})
})
})
}
这个方法中,在判断是否所有的promise实例都敲定时,不能通过resArr的长度来判断,因为我们不知道是哪个promise实例先敲定,如果是后面的某个先敲定,则数组中除了敲定的那个promise的结果外,有可能就会含有空值,这样数组长度就变得不可信,因此我们需要手动定义一个计数器来判断是否全部敲定。
Promise.allSettled()方法和all方法很像,不同之处在于这个方法无论传入数组中的promise时成功还是失败,都会将其结果信息存入数组中,等到所有的promise实例都敲定,就会兑现一个promise实例,结构就是数组中每个项的结果信息。
参考:Promise.allSettled() - JavaScript | MDN (mozilla.org)
代码如下:
static allSettled(arr) {
return new MyPromise((resolve, reject) => {
// 判断是否传入数组参数
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 判断传入的参数是否为空数组
if(arr.length === 0) resolve([])
// 定义一个结果数组
const resArr = []
// 计数器
let count = 0
// 遍历数组
arr.forEach((item,index) => {
MyPromise.resolve(item).then((res) => {
// 添加到结果数组中
resArr[index] = {status: "fulfilled", value: res}
count++
if(count === arr.length) resolve(resArr)
},err => {
// 添加到结果数组中
resArr[index] = {status: "rejected", reason: err}
count++
if(count === arr.length) resolve(resArr)
})
})
})
}
Promise.any()这个方法的功能和all完全相反,它是等待所有的promise都拒绝,然后返回一个拒绝的promise,如果只要有一个成功,则返回一个成功的promise。同时它所返回的拒绝信息本质上是一个AggregateError类型的错误,这个错误中包含了所有的promise拒绝的原因。如果传入空数组,直接将空数组作为原因返回。
MDN参考:Promise.any() - JavaScript | MDN (mozilla.org)
代码如下:
static any(arr) {
return new MyPromise((resolve, reject) => {
// 判断是否为数组
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 判断是否为空数组
if(arr.length === 0) {
const error = new AggregateError([],"All promises were rejected")
return reject(error)
}
// 定义结结果数组
const errArr = []
// 定义一个计数器
let count = 0
// 遍历数组
arr.forEach((item,index) => {
MyPromise.resolve(item).then((res) => {
resolve(res)
},err => {
errArr[index] = err
count++
if(count === arr.length) {
const error = new AggregateError(errArr,"All promises were rejected")
reject(error)
}
})
})
})
}
至此,Promise中所有的实例方法与静态方法全部手写完毕,以下是本次手写promise的完整版代码:
class MyPromise{
state = "pending"
result = null
// then方法传入的回调函数队列
#handlers = [] // [{onFufilled, onRejected}...]
// 执行异步任务
#runAsyncTask(callback) {
if(typeof queueMicrotask === 'function') {
// 1.queueMicrotask
queueMicrotask(callback)
} else if( typeof MutationObserver === 'function' ) {
// 2.MutationObserver
const obv = new MutationObserver(callback)
const div = document.createElement("div")
obv.observe(div, { attributes: true })
div.setAttribute("name", "123")
} else {
// 3.setTimeout
setTimeout(callback, 0)
}
}
#resolvePromise(promise,res,resolve,reject) {
// 判断是否重复引用,如果重复引用则抛出错误
if(res === promise) {
throw new TypeError("Chaining cycle detected for promise #")
}
// 如果返回值时promise实例,则需要单独处理
if(res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
}
// 构造函数
constructor(callback) {
// 为回调函数传入resolve和reject方法
const resolve = (result) => {
// 状态变为resolved
if(this.state === "pending") {
this.state = "fufilled"
this.result = result
// 执行回调队列
this.#handlers.forEach((item) => {
item.onFufilled(this.result)
})
}
}
const reject = (result) => {
// 状态变为rejected
if(this.state === "pending") {
this.state = "rejected"
this.result = result
// 执行回调队列
this.#handlers.forEach((item) => {
item.onRejected(this.result)
})
}
}
// 这里需要处理异常,将异常信息注入catch方法中
try {
callback(resolve, reject)
} catch(err) {
reject(err)
}
}
// then方法
then(onFufilled, onRejected) {
// 判断传入的两个参数是否为函数,如果不是函数,则按照对应的处理逻辑包装成函数
if(typeof onFufilled !== "function") {
onFufilled = (x) => x
}
if(typeof onRejected !== "function") {
onRejected = (x) => { throw(x) }
}
// 创建一个用于返回的promise实例
const newP = new MyPromise((resolve,reject) => {
// 执行对应的回调
if(this.state === "fufilled") {
// 执行异步任务
this.#runAsyncTask(() => {
// 捕获错误
try {
// 接收成功回调的返回值,作为下一个then的参数
const res = onFufilled(this.result)
// 判断是否引用,是否是promise实例
this.#resolvePromise(newP, res, resolve, reject)
} catch(e) {
reject(e)
}
})
} else if(this.state === "rejected") {
// 执行异步任务
this.#runAsyncTask(() => {
try{
const res = onRejected(this.result)
this.#resolvePromise(newP, res, resolve, reject)
} catch(err) {
reject(err)
}
})
} else if(this.state === "pending") {
// 如果此时还是默认状态,则说明构造函数中执行了异步操作,此时需要将回调函数存起来,等到异步操作执行完毕后,再执行回调函数
this.#handlers.push({
onFufilled: () => {
this.#runAsyncTask(() => {
try {
const res = onFufilled(this.result)
this.#resolvePromise(newP, res, resolve, reject)
}catch(err) {
reject(err)
}
})
},
onRejected: () => {
this.#runAsyncTask(() => {
try {
const res = onRejected(this.result)
this.#resolvePromise(newP, res, resolve, reject)
} catch(err) {
reject(err)
}
})
}
})
}
})
// 方法结束返回新的promise实例
return newP
}
// catch方法
catch(onRejected) {
return this.then(null, onRejected)
}
//finally方法
finally(onFinally) {
return this.then(onFinally, onFinally)
}
// 静态方法
static resolve(value) {
// 判断传入的值是否为promise实例
if(value instanceof MyPromise) {
return value
} else {
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
}
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value)
})
}
static all(arr) {
return new MyPromise((resolve,reject) => {
// 判断是否是数组
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 如果是空数组,则直接返回一个成功状态的promise
if(arr.length === 0) resolve([])
// 定一个数组,用于存储结果
const resArr = []
// 兑现次数
let count = 0
// 遍历数组
arr.forEach((item,index) => {
MyPromise.resolve(item).then((res) => {
// 添加到结果数组中
resArr[index] = res
// 计数器+1
count++
// 判断是否全部执行完毕
if(count === arr.length) {
resolve(resArr)
}
},err => {
reject(err)
})
})
})
}
static race(arr) {
return new MyPromise((resolve, reject) => {
// 判断是否传入数组参数
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 等待第一个promise执行完毕
for(let item of arr) {
MyPromise.resolve(item).then((res) => {
resolve(res)
},err => {
reject(err)
})
}
})
}
static allSettled(arr) {
return new MyPromise((resolve, reject) => {
// 判断是否传入数组参数
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 判断传入的参数是否为空数组
if(arr.length === 0) resolve([])
// 定义一个结果数组
const resArr = []
// 计数器
let count = 0
// 遍历数组
arr.forEach((item,index) => {
MyPromise.resolve(item).then((res) => {
// 添加到结果数组中
resArr[index] = {status: "fulfilled", value: res}
count++
if(count === arr.length) resolve(resArr)
},err => {
// 添加到结果数组中
resArr[index] = {status: "rejected", reason: err}
count++
if(count === arr.length) resolve(resArr)
})
})
})
}
static any(arr) {
return new MyPromise((resolve, reject) => {
// 判断是否为数组
if(!Array.isArray(arr)) {
return reject(new TypeError("Argument is not iterable"))
}
// 判断是否为空数组
if(arr.length === 0) {
const error = new AggregateError([],"All promises were rejected")
return reject(error)
}
// 定义结结果数组
const errArr = []
// 定义一个计数器
let count = 0
// 遍历数组
arr.forEach((item,index) => {
MyPromise.resolve(item).then((res) => {
resolve(res)
},err => {
errArr[index] = err
count++
if(count === arr.length) {
const error = new AggregateError(errArr,"All promises were rejected")
reject(error)
}
})
})
})
}
}