Promise
1、介绍
1.1、Promise是什么
Promise是ES6中提出的JavaScript中进行异步编程的一种新的解决方案(旧方案就是使用回调函数)
从语法上来说,就是一个构造函数
从功能上来说,Promise可以封装一个异步操作并过去其成功或失败的结果
1.2、为什么要使用Promise
指定回调函数的方式更加灵活
支持链式调用,能够解决回调地狱的问题
2、Promise使用
2.1 promise的三种状态
- pending,初始化状态
- fulfilled/resolved,成功状态
- rejected,失败状态
当我们创建一个Promise实例时,该实例的状态为pending,promise的状态只能从pending切换到fulfilled或从pending切换到rejected,且只能切换一次。
2.2 如何使用
promise本质上是一个构造函数,所以我们可以通过new关键字来进行使用
promise接收一个回调函数(可以是箭头函数或匿名函数),该回调函数称为执行器函数,在Promise内部同步调用
const p = new Promise(()=>{
console.log("hello Promise")
})
执行器函数传入resolve和reject两个参数,这两个参数是两个函数,可以用来修改promise实例的状态
resolve:将promise实例的状态从pending变成fulfilled
reject:将promise实例的状态从pending变成rejected
除了以上两种方法,还可以通过throw直接抛出错误的方法,将promise的状态修改为失败
const p = new Promise((resolve, reject) => {
const randNum = Math.floor(Math.random() * 10)
if (randNum > 5) {
/*修改promise实例的状态为成功*/
resolve(`success,value=${randNum}`)
} else {
/*修改promise实例的状态为失败*/
reject(`error,value=${randNum}`)
}
})
console.log(p)
2.3 then() 方法
then方法是promise原型对象上的方法,最多接收两个函数,第一个为promise状态为fulfilled的回调函数,第二个为promise状态为rejected的回调函数
const p = new Promise((resolve, reject) => {
const randNum = Math.floor(Math.random() * 10)
/*通过随机数随机修改promise的状态*/
if (randNum > 5) {
/*修改promise实例的状态为成功*/
resolve(`success,value=${randNum}`)
} else {
/*修改promise实例的状态为失败*/
reject(`error,value=${randNum}`)
}
})
p.then(success => {
console.log(success)
},error => {
console.log(error)
})
then方法最终返回一个新的promise,该promise对象的状态和结果均由then方法指定的回调函数的执行结果决定
- 如果回调函数返回一个新的promise,则此promise的结果和状态就会成为then方法返回的promise的结果和状态
- 如果返回的是一个非promise值,结果为这个值,状态为成功(不论是成功回调还是失败回调),如果回调函数没有写返回值,则返回undefined
- 如果抛出错误,新promise的状态为失败,结果为抛出的错误
由于then方法返回一个新的Promise对象,所以支持链式调用
const p = new Promise((resolve, reject) => {
resolve(1)
})
const result = p.then(value => {
console.log(value)
return value
}).then(value => {
console.log(value)
return value
})
result.then(value => {
console.log(value)
return value
})
如果希望中断Promise的链式调用,有且只有一种方法:返回一个pending状态的promise对象
const p = new Promise((resolve, reject) => {
resolve(111)
})
p.then(value => {
console.log(222)
return new Promise(()=>{})
}).then(value => {
console.log(333)
return value
})
2.4 catch() 方法
catch方法是promise原型对象上的方法,接收一个回调函数,并在promise状态为失败时进行调用
const p = new Promise((resolve, reject) => {
const randNum = Math.floor(Math.random() * 10)
/*通过随机数随机修改promise的状态*/
if (randNum > 5) {
/*修改promise实例的状态为成功*/
resolve(`success,value=${randNum}`)
} else {
/*修改promise实例的状态为失败*/
reject(`error,value=${randNum}`)
}
})
p.then(success => {
console.log(success)
}).catch(error => {
console.log(error)
})
**异常穿透:**当我们链式调用then方法时,不必每次都写错误处理函数,只需在最后加上一个.cath方法,即可完成整个调用的错误捕获
const p = new Promise((resolve, reject) => {
reject("error")
})
p.then(value => {
console.log(222)
return value
}).then(value => {
console.log(333)
return value
}).catch(error => {
console.log(error)
})
3、Promise方法
3.1 resolve()
Promise.resolve()方法直接返回一个Promise对象
resolve可以接收一个参数,如果该参数是一个非Promise类型的对象,则返回一个状态为成功的Promise对象
如果该参数是一个Promise类型的对象,则resolve方法放回的Promise对象的状态取决于参数Promise对象的状态
const p1 = Promise.resolve(null)
// 通过resolve创建了一个promise对象,该对象的状态为成功
console.log(p1)
const p2 = Promise.resolve(new Promise((resolve, reject)=>{reject("error")}))
// resolve接受了一个失败状态的promise,所以resolve方法返回的promise对象的状态也是失败
console.log(p2)
3.2 reject()
Promise.reject()方法直接返回一个Promise对象
reject可以接收一个参数,作为promise的结果
该对象的状态一定为失败
3.3 all()
Promise.all()方法接收一个由promise组成的数组,并返回一个Promise
当数组中所有的Promise为成功时,返回一个成功状态的Promise对象,成功的结果就是每一个Promise对象结果组成的一个数组
当数组中有一个Promise为失败时,返回一个失败状态的Promise对象,不论多少个失败的Promise对象,all方法失败的结果就是数组中第一个失败的这个Promise失败的结果
const promiseList1 = [
Promise.reject(1),
Promise.resolve(2),
Promise.resolve(3),
]
const p1 = Promise.all(promiseList1)
console.log(p1)
const promiseList2 = [
Promise.reject(1),
Promise.reject(2),
Promise.resolve(3),
]
const p2 = Promise.all(promiseList2)
console.log(p2)
3.4、race()方法
Promise.race()方法接收一个由Promise组成的数组,并返回一个Promise
返回的promise的状态和结果取决于数组中第一个状态发生修改的Promise
// 定义一个获得0-999随机数的方法,用于随机生成定时器的时间
const randomSleepTime = () => Math.floor(Math.random() * 1000)
// 这是一个promise列表
const promiseList = [
// 定义了一个一定成功的promise,状态在随机时间后发生修改
new Promise(resolve => {
setTimeout(()=>resolve("success"),randomSleepTime())
}),
// 定义了一个一定失败的promise,状态在随机时间后发生修改
new Promise((resolve,reject) => {
setTimeout(()=>reject("success"),randomSleepTime())
})
]
const p = Promise.race(promiseList)
console.log(p)
4、手写Promise
/**
* @author 太阳当空丶赵
* @date 2022/11/20-22:53
* @version 1.0.0
* 手写promise——class版本
*/
/*定义promise状态*/
const stateMenu = {
PENDING: "pending",
RESOLVED: "fulfilled",
REJECTED: "rejected"
}
class MyPromise {
/*设置初始状态为pending*/
promiseState = stateMenu.PENDING
/*初始结果为null*/
promiseResult = null
/*使用一个列表来存储异步方法的回调函数*/
callBackList = []
constructor(executor) {
/*
* 解决类方法中的this指向问题
* 这里的resolve和reject是通过类调用,this不指向实例
* then方法是作为实例的原型方法进行调用,this指向原型
* 除了通过bind修改函数上下文,还可以使用箭头函数
* */
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
/*执行器函数调用,使用try/catch捕获异常,发生异常时直接将promise状态设置为失败*/
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
resolve(data) {
/*如果状态已经被修改,则不做任何操作,确保状态只能被修改一次*/
if (this.promiseState !== stateMenu.PENDING) return null
// 将状态修改为成功
this.promiseState = stateMenu.RESOLVED
// 保存成功的数据
this.promiseResult = data
/*
* 很多时候,promise中执行的并不是同步代码,修改状态的操作可能在then方法执行之后
* 这里是将then方法中保存的回调列表进行遍历,同时执行所有成功的回调
* 为什么是一个回调列表?
* 因为可能不只有一个then方法,
* 所以需要将所有then方法的回调都存到实例上
* */
for (const callBack of this.callBackList) {
callBack.onResolved()
}
}
reject(data) {
if (this.promiseState !== stateMenu.PENDING) return null
this.promiseState = stateMenu.REJECTED
this.promiseResult = data
/*与resolve相同,只不过这里调用的是失败的回调*/
for (const callBack of this.callBackList) {
callBack.onRejected()
}
}
/*then方法可以接收两个回调函数,第一个是成功的回调函数,第二个是失败的回调*/
then(onResolved, onRejected) {
const that = this
/*当不传递错误处理的回调方法时,默认抛出错误,这里是catch能够在链式调用中捕获错误的关键*/
if (typeof onRejected !== "function") {
onRejected = function () {
throw that.promiseResult
}
}
/*当不传入成功处理回调时,默认传入下一次调用,也就是将成功的结果存放到新的promise中*/
if (typeof onResolved !== "function") {
onResolved = value => value
}
/*then方法返回一个promise对象*/
return new MyPromise((resolve, reject) => {
/*不同状态的处理函数*/
function stateHandler(type) {
try {
// 获取回调函数的执行结果
const result = type(that.promiseResult)
// 如果结果是一个promise,那么then方法返回的这个promise的结果由返回的promise决定
if (result instanceof MyPromise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
// 如果结果不是一个promise,直接返回一个成功状态的promise
} else {
resolve(result)
}
} catch (e) {
// 异常捕获
reject(e)
}
}
// 当promise状态为成功时,调用成功的回调,用于同步情况
if (that.promiseState === stateMenu.RESOLVED) {
stateHandler(onResolved)
}
// 当promise状态为失败时,调用失败的回调,用于同步情况
if (that.promiseState === stateMenu.REJECTED) {
stateHandler(onRejected)
}
/*
* 当promise的执行器函数是一个异步方法,
* 在调用then方法时,promise的状态可能还未发生改变,需要在状态发生改变时调用对应的回调函数
* */
if (that.promiseState === stateMenu.PENDING) {
/*
* 将对应的回调函数保存到实例自身,方便在实例状态发生改变的情况调用
* 什么时候发生变化?
* 调用resolve、reject或抛出错误时
* */
that.callBackList.push({
onResolved: () => {
stateHandler(onResolved)
},
onRejected: () => {
stateHandler(onRejected)
}
})
}
})
}
/*
* catch方法用于捕获promise中发生的错误,
* 其原理就是对then方法的封装,
* 直接返回then方法,同时传入错误处理的回调
* */
catch(onRejected) {
return this.then(undefined, onRejected)
}
/*静态方法,通过类直接调用*/
static resolve(data) {
/*
* resolve方法接收一个参数,同时返回一个promise
* */
return new MyPromise((resolve, reject) => {
if (data instanceof MyPromise) {
// 如果是promise,直接调用该promise的then方法并将成功或失败的结果作为返回的promise的结果
data.then(v => resolve(v), r => reject(r))
} else {
// 如果参数是一个非promise值,则返回一个成功状态的promise
resolve(data)
}
})
}
static reject(data) {
/*reject方法直接返回一个失败状态的promise*/
return new MyPromise((resolve, reject) => {
reject(data)
})
}
static all(promiseList) {
/*
* all方法接收一个promise列表
* */
return new MyPromise((resolve, reject) => {
// 定义一个变量用于保存成功的promise的数量
let successNum = 0
let resultList = []
/*通过索引确保每一个promise的结果按照顺序存放到结果数组中,因为任务中可能存在异步任务,直接使用push方法可能导致顺序错乱*/
for (let i = 0; i < promiseList.length; i++) {
promiseList[i].then(result => {
// 如果当前的promise执行成功,则将成功的结果保存到数组里,同时成功数量+1
resultList[i] = result
successNum++
// 当成功的数量与promise数组长度相同时,说明所有promise均执行成功,调用resolve方法
if (successNum === promiseList.length){
resolve(resultList)
}
},reason => {
// 当有一个promise执行失败时,all方法返回的promise也为失败
reject(reason)
})
}
})
}
static race(promiseList) {
/*race方法接收一个promise列表,并返回第一个成功或失败的promise的结果*/
return new MyPromise((resolve,reject) => {
for (const promise of promiseList) {
promise.then(result => resolve(result),reason => reject(reason))
}
})
}
}