一. Promise的产生背景
Promise是ES6中新提出的类,每一个新方法的提出都是为了解决已有痛点。Promise为处理异步事件提供了很好的解决方案
场景
在开发中,通常异步请求函数和处理数据函数并不在同一个文件中,在request.js中发起异步请求,得到返回结果
// 发起异步请求
function requestData(url) {
setTimeout(()=>{
if(url === 'correctURL'){
let value = ['abc', 'cba', 'nba'];
}else{
let reason = "please check your internet";
}
}, 1000)
}
在index.js中调用请求函数如下,如何得到返回数据结果呢
requestData("correctURL");
返回值传递❌
直接在函数中将得到的值返回,但是有问题,由于请求数据是异步操作,所以无论如何都会返回undefined。
function requestData(url) {
let result;
setTimeout(()=>{
if(url === 'correctURL'){
result= ['abc', 'cba', 'nba'];
}else{
result = "please check your internet";
}
}, 1000)
return result; //undefined;
}
// index.js
const result = requestData("correctURL"); // undefined
回调函数✔️
// request.js
function requestData(url, onSuccess, onFail) {
setTimeout(()=>{
if(url === 'correctURL'){
let value = ['abc', 'cba', 'nba'];
onSuccess(value);
}else{
let reason = "please check your internet";
onFail(reason)
}
}, 1000)
}
在index.js中,利用回调函数的入参拿到异步请求返回结果
requestData(
"coderwhy",
(value) => {
console.log("请求成功", value);
},
(reason) => {
console.log("请求失败", reason);
}
);
回调函定义是在index.js文件中;而调用是在request.js异步函数中
回调函数定义和调用的位置不同,相当于把一段数据处理逻辑通过回调函数的方式注入了异步函数,拿到数据之后,执行数据处理
回调函数的弊端
- 如果是自己封装的requestData,需要设计好回调函数,并按要求使用
- 如果是别人封装的requestData,需要去看源码或者文档,才知道如何使用回调函数获取结果
- 回调函数相当于控制反转,调用函数的时机和权利交给别人不安全
- 回调函数这种非顺序的方式和大脑思考方式相悖
Promise规范✔️👍
用Promise类型对象来作为承诺,用同步的方式写异步代码。
// request.js
function requestData(url) {
const promise = new Promise((resolve, reject) => {
setTimeout(()=>{
if(url === 'correctURL'){
let value = ['abc', 'cba', 'nba'];
resolve(value);
}else{
let reason = "please check your internet";
reject(reason)
}
}, 1000)
})
return promise;
}
异步函数也有返回值,返回一个promise承诺,拿到承诺之后,可以通过then方法执行返回成功的逻辑,catch方法执行返回失败的逻辑。
调用异步函数和调用处理异步结果的函数都由index.js控制执行,控制权掌握在自己手里。
// index.js
const promise = requestData("correctURL");
promise.then(res=>{
console.log("请求成功", value);
}).catch(err => {
console.log("请求失败", err);
})
二. Promise的基本使用
Promise的使用需要遵循一套统一的规范,看起来很复杂,其实拆解起来并不难
理解关键
- Promise是一个类,要创建对象就要调用new关键字,传入的参数为一个函数executor
- executor函数有两个入参,为两个回调函数resolve,reject,它们已经在Promise类内部实现好了
- executor函数传入之后会被立即执行
const promise = new Promise((resolve, reject) => {
// 一些操作
resolve('value');
reject('reason');
})
Promise对象的三种状态
- pending:悬而未决,还没执行resolve
- fulfilled:已经完成
- rejected:已被拒绝
状态一旦由pending变为fulfilled或rejected就不会再改变
const promise = new Promise((resolve, reject) => {
resolve('value'); // pending -> fulfilled
reject('reason'); //在这里不会执行,状态一经确定,不会改变
console.log('aaaaaa'); // 会执行
})
Promise对象方法
1 then方法
执行时机:在promise对象状态为fulfilled/rejected时执行
入参:一个(两个)回调函数
返回值:返回一个promise对象,具体取决于onFulfilled或onRejected的返回值
const onFulfilled = res => console.log('成功', res); // res对应着resolve中的参数
const onRejected = err => console.log('失败', err); // err入参对应着reject中的参数
promise.then(onFulfilled, onRejected);
promise.then(onFulfilled)
2 catch方法
在promise对象状态为rejected时执行, 入参一个回调函数
回调函数onRejected入参对应着reject中的参数
const onRejected = err => console.log('失败', err);
promise.catch(onRejected)
3 finally方法
无论状态是什么,最后都会执行finally
入参一个回调函数 onFinally,
回调函数 onFinally没有入参
const onFinally= () => console.log('最后');
promise.catch(onFinally)
Promise类方法
1 resolve
将常量转为Promise对象,如果入参不是常量,而是Promise对象或者thenable的值,那么返回Promise的resolve入参取决于他们的resolve入参
Promise.resolve('aaa')
// 等于下列代码
new Promise((resolve, reject) => {
resolve('aaa');
})
2 reject
返回一个Promise对象,如果入参不是常量,而是Promise对象或者thenable的值,那么返回Promise的reject入参取决于他们的reject入参
const promise = Promise.reject('rejected message');
// 等于下列代码
const promise1 = new Promise((resolve, reject) => {
reject('rejected message')
})
3 all & allSettled
Promise.all:多个promise对象状态变为fulfilled时,才会返回, 如果有一个返回值为reject,所有都会中断
Promise.allSettled:多个对象状态都敲定后,就会返回,返回值有状态标识以及内容
- 参数以数组方式传入
- 返回值也是数组,顺序与入参相同,与返回时间无关
- 入参类型不是Promise对象,会借助Promise.resolve转为Promise对象
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 2000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
//resolve(222);
reject('p2 reject')
}, 1000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333);
}, 3000);
});
Promise.all()
// 注意:'aaa'相当于Promise.resolve('aaa')
const p4 = Promise.all([p1, p2, p3, "aaa"]);
p4.then((res) => {
console.log("res: ", res);
}).catch((err) => {
console.log("err: ", err);
});
// err: p2 reject
Promise.allSettled()
const p4 = Promise.allSettled([p1, p2, p3, "aaa"]);
p4.then((res) => {
console.log("res: ", res);
}).catch((err) => {
console.log("err: ", err);
});
// res
res: [
{ status: 'fulfilled', value: 111 },
{ status: 'rejected', reason: 'p2 reject' },
{ status: 'fulfilled', value: 333 },
{ status: 'fulfilled', value: 'aaa' }
]
4 race & any
Promise.race:返回状态最先敲定的值,可以是resolve也可以是reject值
Promise.any:返回状态最先变为fulfilled的resolve值
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 2000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p2 reject')
}, 1500);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333);
}, 3000);
});
Promise.race()
// 注意:reject和resolve公平竞争,谁快谁先返回
// 虽然程序没停止,但是p4已经有结果了
const p4 = Promise.race([p1, p2, p3]);
p4.then((res) => {
console.log("res: ", res);
}).catch((err) => {
console.log("err: ", err);
});
// err: p2 reject
Promise.any()
const p4 = Promise.any([p1, p2, p3]);
p4.then((res) => {
console.log("res: ", res);
}).catch((err) => {
console.log("err: ", err.errors);
});
// res: 111