Promise
一、概念
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
Promise是ES6新增的引用类型,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果,可以通过new来进行实例化对象。Promise内部包含着异步的操作。
是一个专门用来解决异步 回调地狱 问题的。
二、回调函数
回调函数就是一个被作为参数传递的函数。
// 把函数 b 当作参数传递到 函数 a 中
// 在函数 a 中以行参的方式进行调用
function a(cb) {
cb()
}
function b() {
console.log('我是函数 b')
}
a(b)为什么需要回调函数
- 当我们执行一个异步的行为的时候,我们需要在一个异步行为执行完毕之后做一些事情
- 那么,我们是没有办法提前预知这个异步行为是什么时候完成的
- 我们就只能以回调函数的形式来进行
- 我们并不知道 ajax 请求什么时候完成,所以就要以回调函数的形式来进行
三、回调地狱
当一个回调函数嵌套一个回调函数的时候
就会出现一个嵌套结构
当嵌套的多了就会出现回调地狱的情况
比如我们发送三个 ajax 请求
- 第一个正常发送
- 第二个请求需要第一个请求的结果中的某一个值作为参数
- 第三个请求需要第二个请求的结果中的某一个值作为参数
javascriptajax({ url: '我是第一个请求', success (res) { // 现在发送第二个请求 ajax({ url: '我是第二个请求', data: { a: res.a, b: res.b }, success (res2) { // 进行第三个请求 ajax({ url: '我是第三个请求', data: { a: res2.a, b: res2.b }, success (res3) { console.log(res3) } }) } }) } })类似以上深度嵌套的回调通常被称为**「回调地狱」**,因为它让代码可读性变差且不易维护
当代码成为这个结构以后,已经没有维护的可能了
三、promise
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
Promise将 回调转换成链式调用来解决回调地狱
1.状态
promise 就是一个语法,有三种状态:
pending(进行中)fulfilled(已成功)rejected(已失败)
promise 意思是表示“承诺”,表示其他手段无法改变。
一旦状态确定,就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
2.语法
Promise 构造函数
new Promise((resolve, reject) => {
if(成功){
resolve(value)
}else if(失败){
reject(reason)
}
})- Promise接收一个函数作为参数,这个函数接收两个回调函数,分别为
resolve和reject - 当异步请求成功,就调用
resolve(),并且将promise 的状态改为fulfilled(已成功) - 当异步请求失败,就调用
reject(),并且将promise 的状态改为rejected(已失败) - resolve()和reject()括号里都可以传递形参
Promise请求流程
const p1= new Promise(function (resolve, reject) {
// resolve() 表示成功的回调
// reject() 表示失败的回调
})
p1.then(function (res) {
// 成功的函数
}).catch(function (err) {
// 失败的函数
})栗子
let p1 = new Promise((resolve, reject) => {
var num = Math.random()
console.log(num)
if(num>0.5){
resolve()
}else{
reject()
}
});
p1.then(function(){
console.log("成功了")
}).catch(function(){
console.log("失败了")
})四、promise 原型方法
添加在 Promise对象原型上的方法
1.then()
//原型方法
Promise.prototype.then( )1.一个参数
当promise 的状态为成功
fulfilled,就会调用此方法当使用一个参数写法时,请求失败的操作就可以用
catch()完成
p.then(()=>{...})2.两个参数
第一个回调函数接收一个参数,当promise 的状态为成功,就调用第一个回调函数
- 这个参数是异步请求成功后传回的数据
第一个回调函数接收一个参数,当promise 的状态为失败,就调用第一个回调函数,一般不这么做
- 这个参数是异步请求失败的信息
p.then((value)=>{...},(reason)=>{...})2.catch()
//原型方法
Promise.prototype.catch()- 用于指定发生错误时的回调函数
- 当promise 的状态为失败
rejected,就会调用此方法
p.catch((reason)=>{
console('请求失败'+reason)
})3.finally()
Promise.prototype.finally()用于指定不管 Promise 对象最后状态成功或失败,都会执行的操作
五、promise 静态方法
添加在 promise函数上的方法
1.Promise.resolve(value)
- 如果传入的参数为非promise类型的对象,则返回的结果为成功promise对象
- 如果传入的参数为 Promise对象,则resolve()的结果和参数的结果一致
Promise.resolve()
.then(function(){
console.log('成功了'); //会调用then
}).catch(function(){
console.log('失败了')
})2.Promise.reject(reason)
- reason: 失败的原因
- 说明: 返回一个失败的 promise 对象,返回的结果都是失败的
Promise.reject()
.then(function(){
console.log('成功了');
}).catch(function(){
console.log('失败了') //会调用catch
}3.Promise.all()
用法
Promise.all()方法接收的参数是一个数组,数组中的每一项是一个个的Promise对象promise.all()数组里所有promise都完成后才会resolve,否则rejectpromise.all()返回的结果可以用then()方法取出,也可以用async/await
成功的结果
数组里的promise全部成功才返回resolve()的结果,并以数组形式呈现,数组中的每一项对应的就是 .all数组中的每一项的请求结果返回的值
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},100)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
},200)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(3)
},1000)
})
Promise.all([p1,p2,p3]).then(res=>{ // [ 1, 2, 3 ]
console.log(res);
})失败的结果
有一个失败就返回reject()的结果,并且只返回第一个失败的promise的值
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},100)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(3)
},200)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(4)
},100)
})
Promise.all([p1,p2,p3]).then(res=>{
console.log(res);
}).catch(res=>{
console.log(res);
})4.Promise.race()
用法
Promise.race()方法接收的参数是一个数组,数组中的每一项是一个个的Promise对象
返回的结果
promise.race()返取最快返回的结果promise.race()返回的结果可以用then()方法取出,也可以用async/await
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},100)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
},200)
})
Promise.race([p1,p2]).then(res=>{
console.log(res);
})5.Promise.allSettled()
用法
Promise.allSettled()方法接收的参数是一个数组,数组中的每一项是一个个的Promise对象
返回的结果
Promise.allSettled()会返回数组里所有promise的结果,以数组的形式呈现,数组里是一个个对象,包含每个promise的返回状态和结果
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},100)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
},200)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(3)
},100)
})
Promise.allSettled([p1,p2,p3]).then(res=>{
console.log(res);
})
// [
// { status: 'fulfilled', value: 1 },
// { status: 'fulfilled', value: 2 },
// { status: 'rejected', reason: 3 }
// ]6.Promise.any()
用法
Promise.allSettled()方法接收的参数是一个数组,数组中的每一项是一个个的Promise对象
返回的结果
- 数组中只要有成功
promise就返回成功的结果,并且返回最快的promise结果 - 数组中所有
promise都失败,才会返回失败的结果
成功
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},300)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
},200)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(4)
},100)
})
Promise.any([p1,p2,p3]).then(res=>{ // 2
console.log(res);
}).catch(res=>{
console.log(res);
})全部失败
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(1)
},300)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(2)
},200)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(4)
},100)
})
Promise.any([p1,p2,p3]).then(res=>{
console.log(res);
}).catch(res=>{
console.log(res);;
})
// [AggregateError: All promises were rejected] { [errors]: [ 1, 2, 4 ] }六、Promise关键问题
如何改变 promise 的状态?
- resolve(value): 如果当前是 pending 就会变为 resolved
- reject(reason): 如果当前是 pending 就会变为 rejected
- 抛出异常: 如果当前是 pending 就会变为 rejected
jslet p=new Promise((resolve,reject)=>{ //1.resolve 函数 //resolve('ok') //pending=>fulfilled(resolve) //2.reject 函数 //reject('error') //pending=>rejected //3.抛出错误 throw 'error' })改变 promise 状态和指定回调函数谁先谁后?
- 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
- 如何先改状态再指定回调?
- 在执行器中直接调用 resolve()/reject()
- 延迟更长时间才调用 then()
- 什么时候才能得到数据?
- 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
- 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
promise.then()返回的新 promise 的结果状态由什么决定?
- 简单表达:
- 详细表达:
- 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
- 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
- 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
jslet p=new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('ok') },1000) }) p.then(value=>{ return new Promise((resolve, reject) => { resolve('success') }); }).then(value=>{ console.log(value); //success }).then(value=>{ console.log(value); //undefine,因为上一个then没有返回值 })promise 如何串连多个操作任务?
- promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用
- 通过 then 的链式调用串连多个同步/异步任务
promise 异常传透?
- 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回
- 前面任何操作出了异常, 都会传到最后失败的回调中处理
中断 promise 链?
- 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
- 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
七、自定义封装Promise
Promise封装ajax
function $ajax(path){
return new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest()
xhr.open('get',path)
xhr.send()
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200){
resolve(xhr.responseText)
}else{
reject(xhr.status)
}
}
}
})
}
$ajax('https://api.mcloc.cn/love')
.then(value=>{
console.log(value);
},reason=>{
console.warn(reason);
})Promise封装fs请求
function mineReadFile(path){
return new Promise((resolve,reject)=>{
require('fs').readFile(path,(err,data)=>{
if(err) reject(err)
resolve(data.toString())
})
})
}
mineReadFile(require('path').join(__dirname,'/content.txt')).then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})八、async和await
async/await其实是Promise的语法糖,它能实现的效果都能用then链来实现,它是为优化then链而开发出来的- async是“异步”的简写,
await译为等待,所以async声明function是异步的 - 由async修饰的函数,返回的都是一个
promise
语法:
async修饰函数,await修饰Promise
- async修饰的函数,返回的都是一个Promise
- await可以把Promise的结果直接取出来
await可以取出Promise的返回值
const fun=async()=>{
let res=await new Promise((resolve)=>{
resolve(1)
})
console.log(res)
}
fun()