一,理解
抽象表达:promise是js中进行异步编程的新的解决方案。(旧的是纯粹的回调函数)
具体表达:从语法上来看,promise是一个构造函数。从功能上来说,promise对象用来封装一个异步操作,并可以获取其结果。
异步操作的第一阶段处理方式:回调函数作为参数传入,异步结果作为回调函数的参数传入,回调函数在异步操作后调用,以完成对异步结果的获取和处理。
之前就学习过,异步操作的执行,是无法直接得到执行结果的。
为了拿到结果,人们将函数作为参数来定义异步操作的函数:
类似于上图这样,利用回调函数作为异步操作的参数传入,在异步操作的函数内部调用回调函数,从而取得异步结果(作为回调函数的参数),并做相应的处理。
也就是说,对异步操作的结果处理放在回调函数中了。
但是,若是出现多层回调函数嵌套的情况,就会产生回调地狱。为了解决回调地狱的问题,人们引入了promise。
所以说它是js中进行异步编程的新的解决方案。
异步操作的第二阶段处理方式:promise
回调地狱的产生,原因还是回调函数对结果的处理和异步操作终究还是在一起,并没有把分离。而引入promise的最大作用,就是把异步操作的过程和结果做到了分离,可以用promise.then()来获取和处理异步操作的结果。
异步操作的第三阶段处理方式:异步函数
promise还是有些繁琐,于是es7又推出了async和await,将同步操作以同步的方式书写出来。
二,promise的状态改变
Promise对象只有三种状态。
异步操作“未完成”(pending)
异步操作“已完成”(resolved,又称fulfilled)
异步操作“失败”(rejected)
这三种的状态的变化途径只有两种且不可逆,且一个promise只能改变以此一次状态。
异步操作从“未完成”到“已完成”
异步操作从“未完成”到“失败”。
因此,Promise对象的最终结果只有两种。要么成功,要么失败。于是只会有一种数据产生,成功的结果数据称value,失败的结果数据称reason
异步操作成功,Promise对象传回一个值,状态变为 resolved。
异步操作失败,Promise对象抛出一个错误,状态变为 rejected。
三,promise的基本运行流程
四,promise的基本使用
const p= new Promise((resolve,reject)=>{ //这个回调函数,又被称之为执行器函数,异步操作放在这里面执行,参数是两个函数(内部定义好了的)
//执行异步操作
setTimeout(()=>{
const time=Date.now() //如果当前时间是偶数就代表成功
//如果成功了,就执行resolve(value),value时成功数据,作为resolve函数的参数
if(time % 2 ==0){
resolve('成功啦')
}else{
//如果异步操作失败了,调用reject(reason),reason是作为它的参数
reject('失败惹')
}
},1000)
})
//对异步操作结果的处理
p.then(
value=>{ //接收到成功的value数据 onResolved
console.log('成功的话就会执行这里的函数---',value)
},
reason=>{ //接收到失败的reason数据 onRejected
console.log('失败的话就会执行这里的函数---',reason)
}
)
五,为什么要使用promise
1,指定回调函数的方式更加灵活
旧的纯粹回调函数的形式,必须在启动异步任务前,就指定好回调函数(作为异步操作函数的形参)。而promise,则是启动异步任务,返回promise对象,然后再给promise对象绑定回调函数(甚至可以在异步任务完成后绑定)
例如:使用纯粹的回调函数:
//成功的回调函数
function successCallback(result){
//对result的一系列操作,例如下面这行打印结果
console.log(result)
}
//失败的回调函数
function failureCallback(error){
//对error的一系列操作
console.log(error)
}
//定义异步操作的函数
function createAudioFileAsync(audioSettings,successFun,errorFun){
//针对audioSettings中的一些参数,进行异步操作
//……一系列的异步操作代码,得成功的话,得到result,失败的话得到error
if(成功){
// 执行成功的操作,并将结果传入作为参数
successFun(result)
}else(失败){
// 执行失败后的操作,并将结果传入作为参数
errorFun(error)
}
}
//1.使用纯粹的回调函数
//使用并进行异步操作,直接指定回调函数,作为异步操作函数的实参传入,使得异步操作的结果也在这个异步操作的函数中处理
createAudioFileAsync(audioSettings,successCallback,failureCallback)
// 2. 使用promise
//直接开始异步操作,只是没有在第一时间完成
const promise = createAudioFileAsync(audioSettings)
//在后面指定的回调函数,异步操作和异步操作的结果做到了分离。
promise.then(successCallback,failureCallback)
2,支持链式调用,可以解决回调地狱问题
什么是回调地狱?回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件。
回调地狱的缺点?不便于阅读、不便于异步处理
解决方案?promise链式调用
终极解决方案?async/await
// 1. 回调地狱
doSomething(function(result){
doSomethingElse(result, function(newResult){
doThirdThing(newResult, function(finalResult){
console.log('Got the final result:' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
// 2. 使用promise的链式调用解决回调地狱
doSomething().then(function(result){
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult){
console.log('Got the final result:'+finalResult)
})
.catch(failureCallback)
// 3. async/await:回调地狱的终极解决方案
async function request() {
try {
const result=await doSomething()
const newResult=await doSomethingElse(result)
const finalResult=await soThirdThing(newResult)
console.log('Got the final result'+finalResult)
} catch(error) {
failureCallback(error)
}
}
六,promise的API使用
1、Promise构造函数:Promise(excutor){}
excutor函数:同步执行 (resolve, reject)=>{}
resolve函数:内部定义成功时我们调用的函数 value=>{}
reject函数:内部定义失败时我们调用的函数 reason=>{}
说明:excutor会在Promise内部立即同步回调,异步操作在执行器中执行
2、Promise.prototype.then方法:(onResolved, onRejected)=>{}
onResolved函数:成功的回调函数 (value)=>{}
onRejected函数:失败的回调函数 (reason)=>{}
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的promise对象
3、Promise.prototype.catch方法:(onRejected)=>{}
onRejected函数:失败的回调函数 (reason)=>{}
说明:then()的语法糖,相当于:then(undefined, onRejected)
new Promise((resolve, reject)=>{
//一系列异步操作,得到结果result,成功则调用resolve,失败则调用reject
resolve(result)
// reject(result)
}).then( //对成功的结果的处理,参数是一个函数,此函数的参数value就是result,
value=>{
//函数的内容里面写对异步结果的处理
console.log('onResolved()', value)
}
).catch( //对失败的结果的处理,参数是一个函数,此函数的参数reason就是result,
reason=>{
console.log('onRejected()', reason)
}
)
4、Promise.resolve方法:(value)=>{}
value: 成功的数据或promise对象
说明:返回一个成功/失败的promise对象
5、Promise.reject方法:(reason)=>{}
reason:失败的原因
说明:返回一个失败的promise对象
// 产生一个成功值为1的promise对象
const p1=new Promise((resolve, reject)=>{
resolve(1)
})
// 产生一个成功值为2的promise对象
const p2=Promise.resolve(2) // 语法糖
const p3=Promise.reject(3)
p1.then(value=>{console.log(value)}) //1
p2.then(value=>{console.log(value)}) //2
p3.catch(reason=>{console.log(reason)}) //3
6、Promise.all方法:(promises)=>{}
promises:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
// 产生一个成功值为1的promise对象
const p1=new Promise((resolve, reject)=>{
resolve(1)
})
// 产生一个成功值为2的promise对象
const p2=Promise.resolve(2) // 语法糖
const p3=Promise.reject(3)
// 一个失败的话返回失败的数据,成功的话返回成功的数组
const pAll = Promise.all([p1,p2,p3])
pAll.then(
values=>{
//此时的valuse是一个数组,每一项对应是每个promise的结果【1,2,3】
},
reason=>{
console.log('all onRejected()', reason) // 3
}
)
7、Promise.race方法:(promises)=>{}
promises: 包含n个promise数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
const pRace = Promise.race([p1,p2,p3]) //不知道谁先完成,一旦有人成功,则取它,其他人不要了
pRace.then(
value=>{
console.log('race onResolved()', value)
},
reason=>{
console.log('race onRejected()', reason)
}
)
七,promise的几个关键问题
1,如何改变promise 的状态
resolve:如果当前是pendding就会变为resolved
reject:如果当前是pendding就会变为rejected
抛出异常:如果当前是pendding就会变为rejected
const p= new Promise((resolve,reject)=>{
//resolve(1) promise变为resolved成功状态
//reject(2) promise变为rejected失败状态
//throw new Error('出错了') 抛出异常,promise变为rejected失败异常,reason为抛出的异常
throw 3 //抛出异常,promise变为rejected失败状态,reason为抛出的3
})
p.then(
value=>{}
reason =>{console.log(reason)}
)
p.then(
value=>{}
reason =>{console.log('第二次'+reason)}
)
//两次都可以执行,也就是说,可以对异步操作的结果做不同的处理
2,改变promise状态和指定回调函数谁先谁后?
先指定回调函数,然后才改变状态:
先改变状态,后指定回调函数:
或者,直接比异步操作结束时间还晚,来指定回调函数:
3,promise的回调函数,都是异步执行的
看这段代码,resolve(1)是同步执行的,.then()也是同步执行的,这时候,状态已经改变,回调函数也指定好了,那么它是同步执行的吗?不,它是异步执行的,可以由------先输出从而判断出来。
4,promise.then()返回的新promise的结果状态是由什么决定的?
(1),简单表达:由then()指定的回掉函数的执行结果决定
(2),详细表达:
1,如果抛出异常,新promise变为rejected状态,reason为抛出的异常
2,如果返回的是非promise的任意值,新promise变为resolved,value为返回的值。
3,如果返回的是另一个新的promise,此promise的结果就会变成新promise的结果
new Promise((resolve,reject)=>{
resolve(1)
}).then(
value=>{
console.log('onResolved1()',value)
//return 2
//return Promise.resolve(3)
//return Promise.reject(4)
throw 5
},
reason=>{
console.log('onRejected1()',reason)
}
).then(
value=>{
console.log('onResolved2()',value)
},
reason=>{
console.log('onRejected2()',reason)
}
)
//r啥也不return时,返回结果是:onResolved1() 1 onResolved2() underfined ,因为没有指定value的值
//return 2时,即为返回非promise的任意值,新promise变为resolved,value为返回的值2。于是结果onResolved1() 1 onResolved2() 2
//return Promise.resolve(3)时,返回的是另一个新的promise,此promise的结果就会变成新promise的结果3
//于是返回的结果是:onResolved1() 1 onResolved2() 3
//return Promise.reject(4)和上一个一样,返回的最终结果:onResolved1() 1 onRejected2() 4
//throw 5,如果抛出异常,新promise变为rejected状态,reason为抛出的异常
//最终的结果:onResolved1() 1 onRejected2() 5
由此可以知道,链式写promise中,第一个异步操作的promise失败 了并不会导致后续的promise失败,后续的promise的失败与否还是由新promise决定的。
5,promise的异常传透
1,当使用promises的then链式调用时,可以在最后指定失败的回调。
2,前面的任何操作出了异常,都会传递到最后的失败中处理
6,中断promise链
1,当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数。
2,办法:在回调函数中返回一个pendding状态的promise对象
原理就是返回的新promise没有结果,所以就不会继续调用reject或者resolve,所以就中断了这个链……