Promise
异步编程
Promise又叫期约,我们在讲解Promise之前,希望大家先了解一个概念:异步编程
特别是在JavaScript这种单线程事件循环模型中,同步操作与异步操作更是代码所要依赖的核心机制。异步行为是为了优化因计算机量大而时间长的操作。如果在等待其他操作完成的同时,即使运行其他指令,系统也能保持稳定,那么这样就是务实的
顾名思义:
同步:内存中顺序执行的处理指令
异步:类似于系统中断,异步操作经常是必要的,因为强制进程等待一个长时间的操作(同步操作则必须要等)是绝对不可取的
以往的异步编程模式
在早期的JavaScript中,只支持定义回调函数来表明异步操作完成,串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(回调地狱)
setTimeout可以定义一个在指定时间之后会被调度执行的回调函数,这是它被称作异步函数的原因
所以同样的我们可以用setTimeout来进行很多操作:
设置一个异步返回值
失败处理
嵌套异步回调
但是写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被称作回调地狱。
我们当然希望以一种更优雅的方式去处理异步编程
那就是 Promise
总结:
简单来说Promise就是对异步编程进行优雅封装
正文
综上所述
我们在处理复杂的网络请求或者其他的一些异步操作的时候,同步会导致进程的阻塞,等待进程导致页面无法显示这就太可笑了
并且早期的异步编程代码冗杂可观性极差,这就用到了Promise
Promise本身是一个对象,是一个构造函数,它需要传入一个参数,并且这个参数必须是函数
new Promis(参数)
new Promise(() => {
//用箭头函数的方式传入一个参数
})
传入的这个函数本身包含两个参数resolve和reject
new Promise((resolve,reject)=>{
})
注意:resolve和reject本身又是一个函数
因为传入的是函数,所以我们可以简单的写入一个定时器函数
new Promise((resolve,reject)=>{
setTimeout(() => {
})
})
Promise的三种状态
1.待定(pending)
2.兑现(fulfilled,有时也称为解决,resolve)
3.拒绝(rejected)
待定(pending)就是期约最初始的状态
成功就将待定转换为解决状态
失败就将待定转换为拒绝状态
注意一旦转换为两种状态,就再也无法改变,无论落定为哪种状态都是不可逆的
有两种过度:pending -> fulfilled或者是pending -> rejected
错误机制:
如果错误已经捕获了,那么错误不会继续传递下去 如果错误没有被捕获,那么错误会隐式传递下去,直到有错误处理函数来捕获这个错误
resolve.then, reject.catch
then方法接受一个参数-resolve返回的数据(正常时)
catch方法接收一个参数-reject返回的信息(抛出异常)
我们只需要记住
resolved(解决)时会执行then(继续)
rejected(拒绝)时会执行catch(捕获异常)
简单的实例:
new Promise((resolve,seject)=>{
setTimeout(()={
reject('Error')
//resolve('Success')
},1000)
}).then((res)=>{
console.log(res)
}).catch((err)=>{
console.log(err)
})
Promise的链式编程思想
为了方便大家理解,我们用简单的数据或者输出代替复杂的网络请求
假如有一个需求:
我们需要进行多次网络请求时,并对请求回来的数据进行大量代码处理
new Promise((resolve,reject) => {
setTimeout(()=> {
console.log('这里是第一次网络请求回来的数据处理代码');
console.log('大约有100行');
setTimeout(()=> {
console.log('这里是第二次网络请求回来的数据处理代码');
console.log('大约有100行');
setTimeout(() => {
console.log('这里是第三次网络请求回来的数据处理代码')
console.log('大约有100行')
},1000)
},1000)
},1000)
})
有没有发现,真的是层层嵌套,你现在可能看似逻辑清晰但把数据操作的代码(正常处理+错误捕获)放进去你就会彻底蒙圈了,深层嵌套+数据逻辑处理
我们来看看使用promise
new Promise((resolve,reject) => {
setTimeout(()=> {
resolve()
},1000)
}).then(() => {
console.log('这里是第一次网络请求回来的数据处理代码');
console.log('大约有100行');
return new Promise((resolve,reject) => {
setTimeout(()=> {
resolve()
},1000)
}).then(() => {
console.log('这里是第二次网络请求回来的数据处理代码');
console.log('大约有100行');
return new Promise((resolve,reject) => {
setTimeout(()=> {
resolve()
},1000)
}).then(() => {
console.log('这里是第一次网络请求回来的数据处理代码');
console.log('大约有100行');
})
Promise.all 、Promise.race
我们可能有如下需求:
我们现在有多个网络请求,并且需要所有请求结果都拿到之后进行下一步操作
我们想要判断两个请求都拿到了,该怎么做呢,平常思维:
并且我们并不能判断哪一个网络请求先拿到,如果已经清楚第一个先拿第二个后拿,那我们只需要在第二个做统一处理就好了,但此时我们只能两个都做处理
设置两个默认为false的变量,某一个请求拿到之后赋值为true,最后做判断两个变量同时为true时进行下一步操作
let isResult1 = false
let isResult2 = false
//请求1
$.ajax({
url:'',
success:function(){
console.log('结果1');
isResult1 = true
handleResult()
}
})
//请求2
$.ajax({
url:'',
success:function(){
console.log('结果2');
isResult2 = true
handleResult()
}
})
function handleResult(){
if(isResult1&&isResult2 ){
//同时为true继续执行操作
}
}
我们再来看看Promise.all
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。
let wake = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time / 1000}秒后醒来`)
}, time)
})
}
let p1 = wake(3000)
let p2 = wake(2000)
Promise.all([p1, p2]).then((result) => {
console.log(result) // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
console.log(error)
})
**需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
二、Promise.race的使用
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})