异步任务的处理
这里我从一个实际的例子来作为切入点,我们调用一个函数,这个函数中发送网络请求(我们可以用定时器来模拟),如果发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去,如果发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息。
function requestData(url,successCallback,failtureCallback){
setInterval(() => {
if(url === 'harry'){
let names = ["abc","ccc","cba"]
//将names通过回调函数返回
successCallback(names)
}else{
let errMessage = '请求失败,url错误'
failtureCallback(errMessage)
}
}, 3000);
}
requestData("harry",res => {
console.log(res);
},err => {
console.log(err);
})
这样做有许多的缺点。
- 如果我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称,并且使用好
- 如果我们使用的是别人封装的requestData,或者一些第三方库,那么我们必须去看别人的源码或者文档,才知道这个函数需要怎么获取到结果。
使用Promise就能很好的解决上述的问题,Promise规范好了所有的代码编写逻辑。 用伪代码来描述,就是这样。
function requestData2(){
return "承诺"
}
const chengnuo = requestData()
什么是Promise呢?
Promise是一个类,可以翻译成 承诺、许诺 、期约(红宝书里是这么翻译的,当时看到让我非常的疑惑)。当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象。
Promise的基本使用
Promise以及executor函数
//Promise是一个构造函数,需要用new创建
const promise = new Promise(()=>{ //传入的参数是个回调函数,并且传递进去的同时,立刻执行,这个函数叫做excutor
})
executor函数中的resolve以及reject
const promise = new Promise((resolve,reject)=>{ //excutor函数中还有两个函数,resolve,reject
// console.log('promise传入的函数被执行了');
resolve()
})
promise.then(()=>{ //then方法传入的回调函数,会在Promise执行resolve函数时,被回调
}) //成功,在then中再传入一个回调函数,并且这个回调函数会立刻执行。
.catch(()=>{ //catch方法传入的回调函数,会在Promise执行reject函数时,被回调
})
在异步请求中使用Promise
我们使用Promise改造我们的异步请求
function requestData(url){
return new Promise((resolve,reject)=>{
setInterval(() => {
if(url === 'harry'){
let names = ["abc","ccc","cba"]
resolve(names)
}else{
let errMessage = '请求失败,url错误'
reject(errMessage)
}
}, 3000);
})
}
//then方法传入的回调函数两个回调函数
//>第一个回调函数,会Promise指向resolve函数时候,被回调
//>第二个回调函数,会在Promise指向reject函数时候,被回调
const promise = requestData("harry")
promise.then(res=>{
console.log('请求成功');
console.log(res);
}).catch(err =>{
console.log('请求失败');
console.log(err);
})
//或者这样
// promise.then(res=>{
// console.log('请求成功');
// console.log(res);
// },err=>{
// console.log('请求失败');
// console.log(err);
// })
Promise的三种状态
Promise有三种状态,pending(悬而未决的状态),fullfilled(已敲定状态)和rejected(已拒绝状态),具体看下面的代码。
new Promise((resolve,reject)=>{ //excutor中给promise划分状态
console.log('------');
resolve() //pending阶段(待定状态)(悬而未决的)
//reject
}).then(res => {
console.log("res:",res); //fullfilled/resolved阶段(已敲定状态)
//promise状态一旦没确定,不能更改 比如这里不能reject()
},err => {
console.log("err:",err); //rejected(已拒绝状态)
})
resolve参数
resolve函数中的参数有三种情况
- 普通的值或者对象
- 传入Promise
- 传入一个对象并且这个对象有then方法。
第一种情况,这种参数会直接来到promise的then处。
const promise = new Promise((resolve,reject)=>{
// resolve("aaa")
resolve({name:'harry'})
})
promise.then(res=>{
// console.log("res:",res); //res: aaa
console.log("res:",res); //res: { name: 'harry' }
})
第二种情况,传入Promise。Promise的状态这时会由传入的promise状态来决定。
const promise = new Promise((resolve,reject)=>{
// resolve({name:'harry'}) //**
reject("error message")
})
new Promise((resolve,reject)=>{
resolve(promise)
}).then(res => {
console.log(res); //{ name: 'harry' }
}).catch(err =>{
console.log(err); //error message
})
第三种情况。如果传入一个对象,并且这个对象有实现then方法,那么Promise会指向该then方法,并且由该then方法决定后续状态(thenable)。
const promise = new Promise((resolve,reject) => {
const obj = {
then(resolve,reject){
resolve("resolve message")
}
}
resolve(obj)
})
promise.then(res => {
console.log("res:",res); //res: resolve message
},err => {
console.log("err:",err);
})
Promise对象方法
查看Promise的对象方法
console.log(Object.getOwnPropertyDescriptors(Promise.prototype));
then方法
1.同一个Promise可以被多次调用then方法
const promise = new Promise((resolve,reject)=>{
resolve('hahaha')
})
promise.then(res => {
console.log("res1:",res);
})
promise.then(res => {
console.log("res2:",res);
})
promise.then(res => {
console.log("res3:",res);
})
- then方法传入的回调函数是可以有返回值的 then方法本身也是有返回值的,它的返回值是Promise
1> 如果我们返回的是一个普通值,那么这个普通的值会被作为一个新的Promise的resolve值
const promise = new Promise((resolve,reject)=>{
resolve('hahaha')
})
promise.then(res =>{
return "aaa"
}).then((res)=>{
console.log(res); //aaa
})
// 这其实等价于
return new Promise(resolve =>{
resolve("aaa")
})
2> 如果我们返回的是一个Promise
const promise = new Promise((resolve,reject)=>{
resolve('hahaha')
})
promise.then(res=>{
return new Promise((resolve,reject) => {
setTimeout(()=>{
resolve(11111)
},3000)
})
}).then(res=>{
console.log(res); //3秒后打印 11111
})
3> 如果我们返回的是一个对象,并且该对象实现了thenable
const promise = new Promise((resolve,reject)=>{
resolve('hahaha')
})
promise.then(res=>{
return {
then(resolve,rejcet){
resolve(222)
}
}
}).then(res=>{
console.log("res:",res); //res: 222
})
catch方法
const promise = new Promise((resolve,reject)=>{
reject("reject status")
})
promise.then(undefined,err =>{
console.log('err:',err);
console.log('------------------------');
})
throw new Error()
const promise = new Promise((resolve,reject)=>{
throw new Error("rejected status") //这和reject一样的 Error: rejected status 但是会把所有堆栈调用信息都打印出来了
})
promise.then(undefined,err =>{
console.log('err:',err);
console.log('------------------------');
})
通过catch方法来传入错误的捕获
const promise = new Promise((resolve,reject)=>{
reject("rejected status")
})
//2.通过catch方法来传入错误(拒绝)的捕获
//不过这种规则并不符合promise/a+规范
promise.catch(err => {
console.log("err:",err); //err: rejected status
})
拒绝捕获的问题,这种写法报错的原因。
const promise = new Promise((resolve,reject)=>{
reject("rejected status")
})
promise.then(res => { //相当于两次独立的调用,它们之间没关系
})
promise.catch(err => { //相当于两次独立的调用
})
catch返回值问题
const promise = new Promise((resolve,reject)=>{
reject("rejected status")
})
promise.then(res => {
console.log("res:",res);
}).catch(err => {
console.log("err:",err);
return "catch return value" //也是new了一个Promise
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err); //catch return value
})
finally方法
// es9(2018)新增的一个特性,表示无论promise对象是fullfilled还是rejected最终都会被执行的一段代码
//finally方法是不接受参数的
const promise = new Promise((resolve,reject) => {
// resolve("resolve message")
reject("error message")
})
promise.then(res => {
console.log("res:",res);
}).catch(err => {
console.log("err:",err);
}).finally(()=>{
console.log('finally code excute');
})
类方法
resolve
resolve用法前文说过了,没什么好解释的。传入普通对象,promise,对象中实现then方法都跟前面的规则一样。
// 类方法Promise.resolve
// 1.普通的值
const promise = Promise.resolve({name:'harry'})
//相当于
const promise2 = new Promise((resolve,reject) => {
resolve({name:'harry'})
})
promise.then(res => {
console.log("res:",res);
})
reject
reject注意无论传入什么值,都是一样的。他不会区分几种情况(普通值,promise等等)
// const promise = Promise.reject("reject message")
//相当于
// const promise2 = new Promise((resolve,reject)=>{
// reject("reject message")
// })
//reject注意无论传入什么值,都是一样的。他不会区分几种情况(普通值,promise等等)。
const promise = Promise.reject({
then(resolve,reject){
resolve("1111")
}
})
promise.then(res => {
console.log("res:",res);
}).catch(err =>{
console.log("err:",err); //err: { then: [Function: then] } 它没有帮我调用,直接把值给了catch
})
all方法
//创建多个Promise
const p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("1111")
}, 1000);
})
const p2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("2222")
}, 2000);
})
const p3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("3333")
// reject("3333")
}, 3000);
})
//需求:所有的Promise都变成fullfilled时,再拿到结果; all 传入数组
//意外:在拿到所有结果之前,有一个promise变成rejected,那么整个promise是rejected了。就不会执行then了,会执行catch
Promise.all([p1,p2,p3,"aaa"]).then(res =>{
console.log(res); //等待三秒后返回一个数组,数组值是[ '1111', '2222', '3333', 'aaa' ]
}).catch(err => {
console.log('err:',err); //err: 3333
})
如果我有另外一个需求,不希望有一个reject其他的Promise都中断。那就得用到allSettle
allSettle
//创建多个Promise
const p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("1111")
}, 1000);
})
const p2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("2222")
}, 2000);
})
const p3 = new Promise((resolve,reject) => {
setTimeout(() => {
// resolve("3333")
reject("3333")
}, 3000);
})
//另外一个需求,不希望有一个reject其他的都中断
//allSettled
Promise.allSettled([p1,p2,p3,"aaa"]).then(res =>{
console.log(res);
/** 返回的结果
* [
{ status: 'fulfilled', value: '1111' },
{ status: 'fulfilled', value: '2222' },
{ status: 'rejected', reason: '3333' },
{ status: 'fulfilled', value: 'aaa' }
]
*/
}).catch(err => {
console.log('err:',err);
})
race
//race竞赛
//创建多个Promise
const p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("1111")
}, 1000);
})
const p2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("2222")
}, 2000);
})
const p3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("3333")
// reject("3333")
}, 3000);
})
//竞赛
//race
//只要有一个Promise变成fullfilled状态,那么就结束
//这三个都没结果突然有个reject,这时候拿到的就是reject的结果。
Promise.race([p1,p2,p3]).then(res =>{
console.log('res:',res); //res: 1111
}).catch(err => {
console.log('err:',err);
})
any
//与race类似,但是它会至少等到一个状态fullfilled
//这个node 14不支持,需要用浏览器,是es11才新出的
const p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("1111")
}, 1000);
})
const p2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("2222")
}, 2000);
})
const p3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("3333")
// reject("3333")
}, 3000);
})
Promise.any([p1,p2,p3]).then(res =>{
console.log('res:',res); //res: 1111
}).catch(err => {
console.log('err:',err);
})