Promise
Promise是一个类,可以翻译成承诺、许诺、期约
基本使用方法
// resolve , reject 是两个回调函数// 当我们调用resolve的时候 , 会执行Promise对象的then方法传入的回调函数// 当我们调用reject的时候 , 会执行Promise对象的catch方法传入的回调函数const promise = new Promise((resolve, reject) => { })
Promise的状态一旦被确定下来,无法被更改,resolve、reject两个函数不会代码禁止向下执行,为了防止继续向下执行,要加上return
Promise状态
- 待定
pending - 已兑现
fulfilled - 已拒绝
rejected
resolve不同值的区别
传入普通值或对象,会直接作为then回调的参数
传入另一个
Promise,那么新Promise对象会决定原Promise的状态 ```javascript const promise = new Promise((resolve, reject) => {resolve(new Promise((resolve, reject) => {
reject("我是新的promies, 我要改变之前promise的状态,这里会执行err")
})) });
promise.then(res => { console.log(res) }, err => { console.log(err) })
3.传入一个实现了then方法的对象,则会执行该then方法,并根据then方法的结果来决定`Promise`状态```javascriptconst promise = new Promise((resolve, reject) => {const obj = {name: 'obj',then(resolve, reject) {reject('传入一个对象,对象里面有then方法,会改变之前promise的状态')}}resolve(obj)});promise.then(res => {console.log(res)}, err => {console.log(err)})
then方法
then方法实质是对象上的方法,也就是Promise.prototype.then
1.同一个Promise可以被多次调用then方法
const promise=new Promise((resolve,reject)=>{resolve('abc')})promise.then((res)=>{console.log('res1'+res)})promise.then((res)=>{console.log('res2'+res)})/*res1:abcres2:abc*/
2.then方法传入的回调函数可以有返回值
then方法的返回值会被作为一个新Promise对象的resolve值返回,这也是为何能链式调用Promise的then方法,则返回值的不同情况与上文resolve的三种不同返回值类似
- 如果返回的是一个普通值(函数默认返回值
undefined),那么这个值会作为一个新的Promise的resolve值
const promise=new Promise((resolve,reject)=>{resolve('abc')})promise.then(res=>{return 'aaa'/* 等价于return new Promise((resolve)=>{resolve('aaa')})*/})
- 如果返回一个
Promise对象 ,其依旧将此值作为一个新的Promise对象的resolve值,由返回的Promise对象决定其状态 - 如果返回一个实现了then方法的
Promise对象,同样作为新Promise对象的resolve值,执行then方法,并根据then方法的结果来决定Promise状态
catch方法
then方法可以传入两个回调函数,分别作为正确执行的回调函数和捕获错误的回调函数,但会使代码阅读性不佳
因此类似一种语法糖,可以使用catch方法来传入捕获错误的回调函数,而then方法仅传入正确执行的回调函数
1.当executor抛出错误时,也是会调用错误捕获的回调函数
const promise=new Promise((resolve,reject)=>{// reject('rejected')throw new Error('rejected')})promise.then(undefined,err=>{console.log('err:'+err)})
2.拒绝捕获的问题
多次使用Promise的then/catch方法,捕获是分别进行的,如下则存在没有捕获错误的问题
const promise=new Promise((resolve,reject)=>{reject('rejected')})promise.then(res=>{···}) // 无捕获promise.catch(err=>{···}) // 有捕获/* 正确使用 */// 方式一promise.then(res=>{···},err=>{···})// 方式二promise.then(res=>{···}).catch(err=>{···})
3.catch方法返回值
实际上catch方法也是会返回一个Promise对象,因此类似then方法,catch方法的返回值会作为一个新Promise对象的resolve值返回
finally方法
ES9(2018)中新增特性:表示无论Promise对象为何种状态,最终都会执行的代码
1.finally的返回值
实测没有任何效果,并不影响链式调用,finally方法下一个链式调用的then方法的参数依旧为finally方法的上一个方法返回值
Promise.resolve(),Promise.reject()
Promise.resolve()
相当于new Promise,并且执行resolve操作,不同于一般resolve操作,其返回值不做任何处理直接作为参数
Promise.reject()
同理
Promise.all(),Promise.allSettled()
Promise.all()
所有Promise都变成fulfilled状态后,再拿到结果数组
但当任意 Promise变成rejected状态,则立即变成对应的rejected状态
Promise.all([p1,p2,p3]).then(resArr=>{console.log(resArr) // [p1Res,p2Res,p3Res]}).catch(err=>{console.log(err) // errMessage})
Promise.allSettled()
ES11(ESMA2020)新增Promise API
其目的是弥补promise.all()的缺陷,该方法中所有Promise无论状态如何最终均不影响该Promise结果一定为fulfilled状态
Promise.allSettled([p1,p2,p3]).then(resArr=>{console.log(resArr)/*[{status:'fullfilled',value:p1Res},{status:'rejected',value:p2Res},{status:'fullfilled',value:p3Res},]*/})
Promise.race(),Promise.any()
Promise.race()
race可翻译为竞赛,只看最先状态变化的Promise
该方法只要检测到传入的任意Promise首先变为任意状态,则不再等待其余Promise,直接返回最先状态发生变化的Promise对应的状态和结果
Promise.race([p1,p2,p3]).then(res=>{console.log(res)}).catch(err=>{console.log(err)})
Promise.any()
ES12(ECMA2021)新增Promise API,目的用于弥补Promise.race()缺陷
该方法不同于Promise.race()只等待第一个Promise状态变化,其会等待第一个状态变为fulfilled的Promise
但如果传入的Primise均为rejected状态,也会等待所有状态变化后再返回AggregateError错误(其属性errors可查看所有rejected返回值)
Promise.race([p1,p2,p3]).then(res=>{console.log(res)}).catch(err=>{console.log(err.errors) // [p1Err,p2Err,p3Err]})
简易Promise实现
Promise规范
Promises/A+ (promisesaplus.cn)
拥有规范才能统一使用方法和相关细节,不然每个人实现的Promise可能都不一样
实现代码
/* 状态常量定义 */const PROMISE_STATUS_PENDING='pending'const PROMISE_STATUS_FULFILLED='fulfilled'const PROMISE_STATUS_REJECTED='rejected'class HYPromise{constructor(executor){/* Promise状态属性 */this.status=PROMISE_STATUS_PENDING;/* Promise resolve reject函数参数属性用于保存函数参数,在不同地方调用 */this.value=undefined;this.reason=undefined;/* Promise 回调函数存储结构属性1.由于Promise中可存在异步executor,需先保存then方法传入的回调函数,待异步executor改变Promise状态后再调用2.但理由1中仅需保存一个函数即可,此处为数组的理由是考虑Promise多次调用,存在多次调用then方法,所以由数组储存 */this.onFulfilledFns=[];this.onRejectedFns=[];/* Promise 内置resolve方法 */const resolve=(value)=>{if(this.status===PROMISE_STATUS_PENDING){// queueMicrotask:添加微任务queueMicrotask(()=>{/* 再次判断当前状态,处理executor中改变多次状态但仅第一次有效情况,由此可能executor中产生多个resolve和reject微任务,需再次确认状态为pending */if(this.status!==PROMISE_STATUS_PENDING)return;this.status=PROMISE_STATUS_FULFILLED;/* 保存当前Promise resolve函数参数,为之后情况多次调用 */this.value=value;this.onFulfilledFns.forEach(fn=>{fn(this.value)})})}}/* Promise 内置reject方法 */const reject=(reason)=>{if(this.status===PROMISE_STATUS_PENDING){queueMicrotask(()=>{if(this.status!==PROMISE_STATUS_PENDING)return;this.status=PROMISE_STATUS_REJECTED;this.reason=reason;this.onRejectedFns.forEach(fn=>{fn(this.reason)})})}}/* 处理executor内部抛出错误情况也会触发reject */try{executor(resolve,reject)}catch(err){reject(err)}}/* then方法 */then(onFulfilled,onRejected){/* 解决 onFufilled,onRejected 没有传值的问题并且由于catch其实是捕获的下一个Promise,此处onRejected默认为抛出当前异常,可实现then/catch功能 */onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };/* Promise的then方法返回新的Promise对象,实现链式调用注意此处为箭头函数,因此当前箭头函数的this绑定为then方法作用域的 */return new HYPromise((resolve,reject)=>{/* 处理多次调用情况下当前Promise状态已发生改变 */if(this.status===PROMISE_STATUS_FULFILLED && onFulfilled){try{/* 将当前Promise resolve中保存的this.value调用then方法传入的回调函数 */const value=onFulfilled(this.value);/* 并将回调函数返回的值作为新Promise对象的返回值传入下一then方法中 */resolve(value);}catch(err){/* 若回调函数发生错误则新Promise对象改变为rejected状态 */reject(err);}}if(this.status===PROMISE_STATUS_REJECTED && onRejected){try{const reason=onRejected(this.reason);resolve(reason);}catch(err){reject(err);}}/* 当前Promise为首次手动创建时,就会以pending状态执行当前if语句此时可能由于异步executor或微任务resolve,reject导致暂未确定状态,因此需保存回调函数待之后的调用 */if(this.status===PROMISE_STATUS_PENDING){/* 保存resolve回调函数1.注意若不考虑链式调用的情况下可能只需保存onFulfilled函数即可,但考虑到链式调用此处应封装下onFulfilled函数,由此可以获取到其返回值value,并改变新Promise状态进入链式调用 */this.onFulfilledFns.push(()=>{try{const value=onFulfilled(this.value)resolve(value)}catch(err){reject(err)}})this.onRejectedFns.push(()=>{try{const reason=onRejected(this.reason)resolve(reason)}catch(err){reject(err)}})}})}/* catch方法 */catch(onRejected){/* 其实此处catch其实是对下一个Promise的catch,配合默认onRejected可实现catch方法捕获当前的异常 */return this.then(undefined,onRejected);}/* finally方法 */finally(onFinally) {return this.then(() => { onFinally() },err => { onFinally(); throw err; });}/* 静态resolve方法 */static resolve(value){return new HYPromise((resolve)=>resolve(value));}/* 静态reject方法 */static reject(reason){return new HYPromise((resolve,reject)=>reject(reason));}/* 静态all方法 */static all(.){}}
