一、基本概念
(1)promise是javascript提供的标准化异步管理方案。总体思想是,等待某些任务执行,不直接返回结果,而 是返回一个’承诺’,可以根据这个对象的状态选择合适的时机进行后续操作。
(2)传统回调、promise、async和await比较。
a.回调函数面临着复杂嵌套的困境,看起来复杂,给开发和维护制造了麻烦。
b.promise比起传统的回调,使用链式操作更人性化,而且提供了方便的api。
c.async和await,利用promise直接,可以将链式调用彻底改为同步写法,异步操作最人性化的写法。
二、promise核心用法
promise的核心在于状态,实例创建后的状态为pending,之后状态可以转变为fullfilled(调用resolve方法)和rejected(调用reject方法或者抛出异常),状态不可逆转。想要在promise为fullfilled时做后续操作,可以调用then方法传入第一个参数,promise为rejected时可以给then方法传入第二个函数参数,
使用构造函数Promise创建一个实例,接收一个excutor函数
excutor: javascript会将两个状态改变函数(resolve,reject)传入excutor作为参数,执行excutor时,可以选择调用resolve方法将promise状态改为resolved,可以调用reject函数或者抛出异常将promise状态改为rejected。如果如果没有处理rejected的promise,promise会抛出异常( uncaught (in promise))。
resolve和reject中可以返回值作为then和catch的参数函数的参数。
let myPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('resolved')
},2000)
});
//无需在promise创建时就调用then和catch方法,可以自己选择时机进行后续处理
myPromise.then((res)=>{console.log(res)},(error)=>{console.log(error)})
在excutor中抛出异常相等于reject( new Error() )
promise状态改变以后,抛出的异常不会被捕获,因为promise的状态一旦改变就永久保持该状态
let myPromise = new Promise((resolve, reject)=>{
throw new Error('rejected error')
//等价于
reject(new Error('rejected error'))
//但是后面的reject函数不会执行,因为状态只能改变一次
})
resolve方法处理一个promise时:当前promise状态由resolve处理的promise决定,在参数promise状态改变后当前promise状态由pending变为参数promise状态。
let p1 = new Promise((resolve, reject)=>{
setTimeout(()=>{
reject(new Error('p1 rejected'))
},5000)
}),
p2 = new Promise((resolve)=>{
setTimeout(()=>{
//当调用resolve处理另一个promise时,当前promise需要等待前一个promise状态改变,
//且状态由参数promise决定
resolve(p1)
},1000)
});
p2
.then((res)=>{console.log('fullfilled', res)})
.catch((error)=>{console.log('rejected', error)}) // 'p1 rejected'
三、Promise.prototype
(1)Promise.prototype.then - promise状态改变后可执行的函数
promise.prototype.then的第一个参数函数用作promise为fullfilled状态的回调方法。
promise.prototype.then的第二个参数函数用作promise为rejected状态和捕获异常(等价于 Promise.prototype.catch)的回调方法。
Promise.prototype.then执行后,会默认返回新的一个状态为fullfilled的promise,也可以返回一个自定义的promise实例。
(2)Promise.prototype.catch
用作rejected状态的promise回调处理,也可用于捕获promise内部异常。promise的异常有类似冒泡的行为机制,promise内部错误(excutor和then方法中的错误)会向后传递,所以promise链式调用时catch方法可以捕获前面的错误。
所以更推荐使用.catch方法,then方法第二个参数函数只能捕获当前promise错误,.catch方法可以捕获前面的promise错误。
promise内部错误不会影响外部代码执行。
catch和then返回值一样,默认返回fullfilled的promise,或者自己返回新的promise。
const promise = new Promise(function (resolve, reject) {
resolve('ok');
setTimeout(function () {
console.log('timeout')
let mypromise = new Promise((resolve)=>{
console.log('promise in timeout')
resolve()
}).then((res)=>{
console.log('microtask in timeOut')
})
throw new Error('test')
}, 0)
});
promise.then(function (value) { console.log(value) }).catch((error)=>{console.log(error)});
//setTimeout是在下一个宏任务,promise在上一轮宏任务中已经执行完毕,
//所以setTimeout是在下一个事件轮询中,promise外部抛出的异常
//所以catch方法捕获不到异常
catch也可以链式调用
let myPromise = new Promise((resolve, reject)=>{
reject(x + 2)
});
myPromise.catch((error)=>{
console.log(error, 'first catch')
reject( y + 2)
}).catch((error)=>{
console.log(error, 'seconde catch')
})
(3)Promise.prototype.finally
无论promise状态是fullfilled还是rejected,finally都会执行,且finally回调参数不接受任何值,所以finally的执行与promise状态和计算结果没有关系。
四、使用promise封装ajax请求函数
//基本参数: 方法 string、url string、headers Array
function promiseForAjax(method,url,params,headers){
return new Promise((resolve, reject)=>{
//验证参数
if(!method || !url){
//如果参数验证不通过,抛出异常也会改变promise状态
throw new Error('请传完整参数')
}
let request = new XMLAndHttpRequest();
request.open(method, url, true);
//创建readyState监听函数
function readyStateListener(request){
if(request.readyState !== 4) return
let response = request.responseText;
if(request.status === 200){
resolve(response)
}else(
reject(response)
)
}
//绑定状态监听函数
request.readyStateChange = readyStateListener
//如果有header,设置header
if(headers && Object.toString.call(headers) === '[obejct Object]'){
let headerArr = Object.entries(headers)
headerArr.forEach(([key,value])=>{
request.setRequestHeader(key,value)
})
}
//计算参数字符串
if(params){
let paramsArr = Object.entries(params),
paramStr = '';
paramsArr = paramsArr.reduce((accumulotar,currentArr)=>{
accumulotar.push(currentArr.join(','))
},[]);
paramStr = paramsArr.join('&')
}
//发送请求
request.send(paramStr) //此处可以传参数 'key=value&key=value'形式
})
}
五、Promise异步管理方法
(1)Promise.all,用来管理多个promise实例,返回一个新的promise。
直到所有promise状态改变后,返回的实例状态才会改变:如果管理的promise都为fullfilled,返回的promise状态才为fullfilled,不然就为rejected。
参数:具有iterator接口的集合,如果元素不是promise,会先调用Promise.resolve来包装。
返回值:
如果是fullfilled,那么返回管理的promise返回值集合,如果是rejected,返回第一个rejected的promise返回值。
在需要等待两个不同后台接口都返回数据后的情况,用promise.all就很方便。
(2)Promise.race,管理多个promise,只要一个promise状态改变,返回的promise状态就改变。
参数同Promise.all
返回值,第一个改变状态的promise返回值。
六、快速创建promise实例
(1)快速创建一个resolved的promise - Promise.resolve
根据传入的参数类别,返回一个resolved的promise
1、传入一个promise,原封不动地返回这个实例
2、如果传入一个有then方法的对象,创建一个promise,并执行then方法。
let thenable = {
then(resolve, reject){
resolve('thenable')
}
}
let myPromise = Promise.resolve(thenable);
console.log(myPromise) // Promise {<fulfilled>: "thenable"}
3、如果传一个不是thenable的对象,返回一个fullfilled的promise,并将参数传给then的回调函数。
4、如果不传参数,则返回一个fullfilled的promise
(2)快速创建一个rejected的promise - Promise.reject
参数会原封不动地传递给catch回调函数