一、Promise
1、什么是promise
抽象表达:
1、Promise是一门新的技术(ES6规范)
2、Promise是JS中进行异步编程的新解决方案
备注:旧方案是单纯使用回调函数
具体表达:
1、从语法上来说:Promise是一个构造函数
2、从功能上来说:Promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
- 具体表达
//1、实例化时,传递一个回调函数(执行器函数),同时也会执行
//2、传递的回调函数可以称其为执行器函数,执行器函数是同步执行的,为同步回调函数
//3、得到的p1是一个基于Promise的实例,该实例中拥有两个属性
// [[PromiseState]]: "pending" 指明了promise的状态
// [[PromiseResult]]: undefined 批明了promise的值
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
//取当前时间UTC
const time=Date.now();
//4、可以通过resolve以及reject改变promise的状态,以及值
//判断是否能被2整除
if(time%2){
//4.1、执行reject,将状态更改为失败:rejected,传入的参数就是[[PromiseResult]]属性值。
//当为奇数时,将状态更改为成功,值为获得的数字time
console.log('奇数',time);
resolve(time);
}else{
//4.1、执行resolve,将状态更改为成功:fulfilled,传入的参数就是[[PromiseResult]]属性值。
//当为偶数时,将状态更改为失败,值为获得的数字time
console.log('偶数',time)
reject(time)
}
})
})
console.log(p1)
//5、p1实例的原型对象中有一个属性then,类型是一个函数。该函数可以接受两个回调函数
p1.then((value)=>{
console.log('成功的回调',value)
},(reason)=>{
console.log('失败的回调',reason)
})
- then语法
//基本语法形式
p.then(onFulfilled[, onRejected]);
//参数介绍:
//p: promise的实例对象
//onFulfilled:是Promise的成功情况下的回调函数
//onRejected:是Promise的失败情况下的回调函数
p.then((value) => {
// fulfillment 里面的参数为成功的值。类似(resolve(value))
console.log(value);
}, (reason) => {
// rejection 里面的参数为成功的值。类似(reject(reason))
console.log(reason)
});
2、promise的状态改变
const p1=new Promise((resolve,reject)=>{
resolve(1);//将状态更改为成功,值为1
reject(2);//将状态更改为失败,值为2
})
console.log(p1);
//成功的结果数据一般称为value,对应的回调函数是onFulfillment
//失败的结果数据一般称为reason,对应的回调函数是onReject
p1.then((value)=>{
console.log(value);
},(reason)=>{
console.log(reason)
})
- 注意
pending是初始状态
1、pending变为fulfilled
2、pending变为rejected
说明:只有这两种,且一个promise对象只能改变一次
无论变为成功还是失败,都会有一个结果数据
成功的结果数据一般为value,失败的结果数据一般称为reason
- 一个promise对象只能改变一次的案例
const p1=new Promise((resolve,reject)=>{
resolve(100);//将状态更改为成功,值为100
reject(500);//不会执行到这一行代码
//所以结果是
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: 100
})
3、更改状态的三种方式
// 1、通过resolve将状态更改为成功
const p1 = new Promise((resolve,reject)=>{
resolve(1);
})
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 1
console.log(p1);
// 2、通过reject将状态更改为失败
const p2 = new Promise((resolve,reject)=>{
reject(2);
})
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: 2
console.log(p2);
// 3、异常,在执行器函数中,如果发生异常,状态为失败,值为异常信息
const p3 = new Promise((resolve,reject)=>{
const a = 1;
a();
})
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: TypeError: a is not a func
console.log(p3);
// 手动抛出异常
const p4 = new Promise((resolve,reject)=>{
throw "发生异常啦"
})
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: 发生异常啦
console.log("p4",p4);
4、promise的基本流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CbGKVVMZ-1629900977961)(C:\Users\12749\AppData\Roaming\Typora\typora-user-images\image-20210825195812110.png)]
// 1、在实例化时,必须要传入回调函数(执行器函数),否则异常
// const p1 = new Promise();
// console.log(p1);
// 2、传入了执行器函数,但是并没有更改状态,那么得到p2的状态为pending
// const p2 = new Promise(function (){
//
// })
// // [[PromiseState]]: "pending"
// // [[PromiseResult]]: undefined
// console.log(p2);
// 3、执行器函数,可以执行同步也可以执行异步。
// 执行同步
// const p3 = new Promise(function (resolve,reject){
// let a = 1;
// let b = 2;
// resolve(1);// 状态成功,值为1
// })
// // [[PromiseState]]: "fulfilled"
// // [[PromiseResult]]: 1
// console.log(p3);
// 执行异步
// const p3 = new Promise(function (resolve,reject){
// setTimeout(()=>{
// let a = 1;
// let b = 2;
// resolve(1);// 状态成功,值为1
// })
// })
// // [[PromiseState]]: "fulfilled"
// // [[PromiseResult]]: 1
// console.log(p3);
// 4、then函数,返回的是一个新的Promise实例
const p4 = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(200);
resolve(100);
})
})
const p5 = p4.then(function (value){
console.log(value);
},function (reason){
console.log(reason);
})
console.log(p5)
二、为什么要使用Promise
1、指定回调函数的方式更加灵活
- 旧的:必须在启动异步任务前指定
- promise:启动异步任务=>返回promise对象 =>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
//1、回调函数:必须在启动异步任务前指定(调用函数时,必须要传入一个回调函数)
const fn=(cb)=>{
setTimeout(()=>{
let a=100;
cb(a);
},1000)
}
//调用之前,指定回调函数
fn((num)=>{
console.log(num)
});
//2、启动异步任务
new Promise(()=>{
setTimeout(()=>{
let a=100;
console.log(a)
},1000)
})
//3、启动异步任务=>返回promise对象
const p1=new Promise(()=>{
setTimeout(()=>{
let b=100;
console.log(b);
})
})
console.log(p1);//p1是一个promise对象
//4、启动异步任务 => 返回promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
let c=200;
reject(c)
},1000)
})
//可以在异步任务结束后指定/多个
p1.then(value=>{
console.log('1',value);//100
},reason=>{
console.error('1',reason);//200
})
p1.then(value=>{
console.log('2',value);//100
},reason=>{
console.error('2',reason);//200
})
p1.then(value=>{
console.log('3',value);//100
},reason=>{
console.error('3',reason);//200
})
2、支持链式调用,可以解决回调地狱问题
- 什么是回调函数?
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
- 回调地狱的缺点?
不便于阅读
不便于异常处理
- 解决方案?
promise链式调用
- 终极解决方案?
async / await
三、如何使用Promise
1、API
- ①、Promise构造函数:Promise (excutor){ }
(1)、executor函数:执行器(resolve,reject)=>{}
(2)、resolve函数:内部定义成功时我们调用的函数 value=>{ }
(3)、reject函数:内部定义失败时我们调用的函数 reason=>{ }
说明:executor会在Promise内部立即同步调用,异步操作在执行器中执行
//执行器函数会被 立即调用
const p1=new Promise((resolve,reject)=>{
console.log(1);
setTimeout(()=>{
//此函数为executor函数
resolve(100);//状态为成功,值为100
})
})
console.log(p1)
console.log(2)
输出结果
// 1
//Promise {<pending>}
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: 100
//2
- ②、Promise.prototype.then方法:(onResolved,onRejected)=>{}
(1)、onResolved函数:成功的回调函数(value)=>{ }
(2)、onRejected函数:失败的回调函数(reason)=>{ }
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败的回调
返回一个新的promise对象
//then 函数是在原型对象当中
const p1 =new Promise((resolve,reject)=>{
resolve(100);//成功,值为100
})
const p2=p1.then((value)=>{
//当状态为成功时,执行成功回调函数,接收的参数为成功的值。这是onResolve函数
console.log(value)
},(reason)=>{
//当状态为失败时,执行失败的回调函数,接收的参数为失败的值。这是onRejected函数
console.log(reason);
});
console.log(p2); //这是一个新的promise
- ③、Promise.prototype.catch方法: (onRejected) => {}
(1) onRejected函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
// 1、如果状态为失败,必须要对失败的内容进行处理,否则异常:Uncaught (in promise) 100
// const p1 = new Promise(function (resolve,reject){
// reject(100);// 状态为失败,值为100
// })
// 2、处理异常方案一
// const p1 = new Promise(function (resolve,reject){
// reject(100);// 状态为失败,值为100
// })
// p1.then(undefined,function (reason){
// console.log(reason);
// })
// 3、处理异常方案二
// const p1 = new Promise(function (resolve,reject){
// reject(100);// 状态为失败,值为100
// })
// // catch传递的是一个失败的回调函数,可以认为是then(undefined, onRejected)
// p1.catch(reason => {
// console.log(reason);
// })
// 4、如果使用then当中的失败回调,也使用catch会怎样。
const p1 = new Promise(function (resolve,reject){
reject(100);// 状态为失败,值为100
})
// 如果then函数中没有指定失败回调,那么会执行catch当中的失败回调。如果catch没有失败回调报异常。
// p1.then(undefined).catch(reason=>{
// console.log(reason)
// });
// 如果then函数中指定了失败回调,那么不会执行catch当中的失败回调
p1.then(undefined,reason=>{
console.log("then",reason);
}).catch(reason=>{
console.log("catch",reason);
})
- ④、Promise.resolve方法:(value)=>{}
(1) value: 成功的数据或promise对象
说明: 返回一个成功/失败的promise对象
// 1、未传入参数,得到的p1是一个promise.状态为成功fulfilled,值为undefined.
// const p1 = Promise.resolve();
// console.log(p1);
// 2、传入一个非promise的数据,状态为成功fullfilled,值为传入的内容。
// const p2 = Promise.resolve({a:1});
// console.log(p2); //{a:1}
// 3、传入一个成功状态的promise:得到的是一个成功状态,值为200的promise
// 得到的p4是一个promise,该promise的状态以及值与p3相同。
// const p3 = new Promise((resolve,reject)=>{
// resolve(200);
// })
// const p4 = Promise.resolve(p3);
// console.log(p4===p3);// true
// 4、传入的是一个失败的promise,那么得到的p6即是该promise
const p5 = new Promise((resolve,reject)=>{
reject(100);
})
const p6 = Promise.resolve(p5);
console.log(p6===p5);// true
- ⑤、Promise.reject方法: (reason) => {}
(1) reason: 失败的原因
说明: 返回一个失败的promise对象
// 不管传入的是什么,得到的都失败的promise。传入的内容即是promise失败的值。
// 1、失败,值为undefined
// const p1 = Promise.reject();
// console.log(p1);
// 2、传入一个非promise,失败状态,值为2
// const p2 = Promise.reject(2);
// p2.catch(reason=>{
// console.log(reason)
// })
// 3、传入成功的promise,p4是状态为失败,值是一个promise(p3)
// const p3 = Promise.resolve(2);
// const p4 = Promise.reject(p3);
// // console.log(p4);
// p4.catch(reason=>{
// console.log(reason===p3);// true
// })
// 4、传入一个失败的promise,得到的状态为失败,值为该失败promise
const p5 = Promise.reject(2);// 失败的promise,值为2
const p6 = Promise.reject(p5);
// console.log(p6);
p6.catch(reason=>{
console.log(reason===p5);// true
})
- ⑥、Promise.all方法: ([promises]) => {}
(1) promises: 包含n个promise的数组
说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败(同步情况下)
const p1 = new Promise(function (resolve){
setTimeout(function (){
resolve(1);
},100)
})
const p2 = new Promise(function (resolve,reject){
setTimeout(function (){
reject(2);
},200)
})
const p3 = new Promise(function (resolve){
setTimeout(function (){
resolve(3);
},300)
})
const p4 = Promise.resolve(4);
// all方法返回的是一个promise,传入的是一个数组。数组的元素建议为promise.
// 状态:如果数组中的元素都是成功的promise,那么状态为成功。如果传入的数组元素有失败的promise,那么状态为失败
// 值:是一个数组,数组为所有成功promise的成功值。如果状态失败,那么值为失败的值。
const p5 = Promise.all([p1,p2,p3,p4,100]);
console.log(p5);
p5.then(value=>{
console.log(value);
}).catch(reason=>{
console.log("失败",reason);
})
- ⑦、Promise.race方法: (promises) => {}
(1) promises: 包含n个promise的数组
说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态
2、Promise的几个关键问题
- ①、如何改变promise的状态?
(1)、resolve(value):如果当前是pending就会变为resolved
(2)、reject(reason):如果当前是pending就会变为rejected
(3)、抛出异常:如果当前是pending就会变为rejected
// (1) resolve(value): 如果当前是pending就会变为resolved
// const p1 = new Promise((resolve,reject)=>{
// resolve(100);// 将状态更改为成功,值为100
// })
// console.log(p1);
// (2) reject(reason): 如果当前是pending就会变为rejected
// const p1 = new Promise(function (resolve,reject){
// reject(100);// 将状态更改为失败,值为100
// })
// console.log(p1);
// (3) 抛出异常: 如果当前是pending就会变为rejected
const p1 = new Promise(function (resolve,reject){
throw "异常"
})
console.log(p1);
- ②、一个promise指定多个成功/失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
const p1 = new Promise(function (resolve,reject){
reject(100);
})
p1.then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})
p1.then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})
p1.then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})
p1.then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})
- ③、改变promise状态和指定回调函数谁先谁后?
(1)、都有可能,正常情况下是先指定回调在改变状态,但也可以先改变状态在指定回调
(2)、如何先改状态在指定回调?
Ⅰ、在执行器中直接调用resolve() / reject()
Ⅱ、延迟更长时间才能调用then()
(3)、什么时候才能得到数据?
如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
// (1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
// 正常情况下,执行器函数中是异步的行为。
// const p1 = new Promise(function(resolve,reject){
// setTimeout(()=>{
// resolve(100);
// },1000)
// })
// p1.then(function (value){
// console.log(value);
// })
// (2) 如何先改状态再指定回调?
// ① 在执行器中直接调用resolve()/reject()
// const p1 = new Promise(function (resolve,reject){
// resolve(200);
// })
// p1.then(value=>{
// console.log(value);// 200
// })
// ② 延迟更长时间才调用then()
// const p1 = new Promise(function (resolve,reject){
// setTimeout(()=>{
// resolve(300);
// },1000)
// })
// setTimeout(()=>{
// p1.then(value=>{
// console.log(value);
// })
// },2000)
// (3) 什么时候才能得到数据?
// ① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
// const p1 = new Promise(function (resolve,reject){
// setTimeout(()=>{
// resolve(80);
// },1000)
// })
// p1.then(value=>{
// console.log(value);// 80
// })
// ② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
const p1 = new Promise(function (resolve,reject){
resolve(80);
})
// 在then方法中会判断当前的状态如果已经发生改变,那么会立即调用对应的回调函数。
p1.then(value=>{
console.log(value);// 80
})
- ④、promise.then()返回的新prommise的结果状态由什么决定
(1)、简单表达:由then()指定的回调函数执行的结果决定
(2)、详细表达:
如果抛出异常,新的promise变为rejected,reason为抛出的异常
如果返回的是非promise的任意值,新promise变成resolved,value为返回的值
如果返回的是另一个新promise,此promise的结果就会称为新promise的结果
// const p1 = new Promise(function (resolve,reject){
// resolve(1);// 状态为成功,值为1
// })
// // p2的状态与值,由谁决定?答:由回调函数的返回值决定。
// const p2 = p1.then(value=>{
// console.log(value);// 1
// })
// console.log(p2);
const p1 = new Promise(function (resolve,reject){
resolve(1);// 状态为成功,值为1
})
// 1、如果抛出异常, 新promise(p2)变为rejected, reason为抛出的异常
// const p2 = p1.then(value=>{
// throw "异常"
// })
// console.log(p2);
// p2.catch(reason=>{
// console.log(reason);
// })
// 2、如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
// const p3 = p1.then(value=>{
// return 100;
// })
// console.log(p3);// 是一个状态为成功,值为100的 promise
// 3、如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
// const p4 = p1.then(value=>{
// // 返回的是一个成功的promise.得到的p4为成功,值为100的promise
// // return Promise.resolve(100);
//
// // 返回的是一个失败的promise,得到的p4为失败,值为200
// return Promise.reject(200);
// })
// console.log(p4);
// 4、then函数得到的是一个新promise
const p = Promise.resolve(50);
const p5 = p1.then(value=>{
return p;
})
console.log(p5 === p);// false
- ⑤、promise如何串联多个操作任务?
(1)、promise的then()返回一个新的promise,可以形成then()的链式调用
(2)、通过then的链式调用串连多个同步/异步任务
- ⑥、promise异常穿透?
(1)、当使用promise的then链式调用时,可以在最后指定失败的回调
(2)、前面任何操作出了异常,都会传到最后失败的回调中处理
- ⑦、中断promise链?
(1)、当使用promise的then链式调用时,在中间中断,不在调用后面的回调函数
(2)、办法:在回调函数中返回一个pending状态的promise对象
新promise的结果
// const p4 = p1.then(value=>{
// // 返回的是一个成功的promise.得到的p4为成功,值为100的promise
// // return Promise.resolve(100);
//
// // 返回的是一个失败的promise,得到的p4为失败,值为200
// return Promise.reject(200);
// })
// console.log(p4);
// 4、then函数得到的是一个新promise
const p = Promise.resolve(50);
const p5 = p1.then(value=>{
return p;
})
console.log(p5 === p);// false
- ⑤、promise如何串联多个操作任务?
(1)、promise的then()返回一个新的promise,可以形成then()的链式调用
(2)、通过then的链式调用串连多个同步/异步任务
- ⑥、promise异常穿透?
(1)、当使用promise的then链式调用时,可以在最后指定失败的回调
(2)、前面任何操作出了异常,都会传到最后失败的回调中处理
- ⑦、中断promise链?
(1)、当使用promise的then链式调用时,在中间中断,不在调用后面的回调函数
(2)、办法:在回调函数中返回一个pending状态的promise对象