一、Promise简单介绍与基本使用
1.1 Promise是什么?
promise是js中进行异步编程的新的解决方案。从语法层面来说,Promise是一个构造函数;从功能上来说,我们使用promise对象来封装一个异步操作并获取其成功/失败的结果值。简单说promise对象就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
可以把promise对象想象成一个冰块,里面冻着一个梨子(异步任务),随着时间推移,冰块化了(异步任务结束,状态改变了),里面的梨子爆露出来了,之后就一直是这个梨子(promise对象状态不会再变了)
1.2 为什么要使用Promise?
1.2.1 指定回调函数的方式更加灵活
- 旧的方式:必须在异步任务开始前,进行指定。
const fs = require("fs");
function read() {
fs.readFile("./a.ts", (err, data) => {
console.log(data);
});
}
read();
- Promise:异步任务启动 => 返回Promise对象 => 给Promise对象指定回调函数(甚至可以在异步任务结束后指定多个回调函数)(备注:指的就是执行then,在then中指定回调函数)(异步任务启动:指的就是在excutor函数同步执行时,其中的异步任务启动)
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 3000);
});
// ........
// 可以马上指定,也可以很久之后指定,都是一样的
// ........
// 指定回调1
p.then((value) => {
console.log(value);
});
// 指定回调2
p.then((value) => {
alert(value);
});
1.2.2 支持链式调用,可以解决回调地狱问题
-
什么是回调地狱? 回调函数嵌套调用,外部回调函数执行的
结果
是嵌套函数执行的条件
。
-
回调地狱的缺点?不便于阅读理解,不便于异常处理
1.3 Promise的状态改变
1.3.1 PromiseState
PromiseState是Promise对象的实例属性,表示Promise对象的状态。
该属性是原生代码内置的,无法在外部手动直接修改,只有resolve和reject函数可以修改此值。
可由初始状态pending => fulfilled,或由初始状态pending => rejected。
只有这两种状态变化,且只能改变一次。
无论成功还是失败都会有一个返回结果,一般把成功的结果数据称为value,失败的结果数据称为reason。
1.3.2 PromiseResult
PromiseResult是Promise对象的实例属性,表示Promise对象的结果值。保存的是异步任务失败/成功的结果,只有resolve和reject函数可以设定此值。
1.3.3 Promise的工作流程
注意:当在excutor函数中throw抛出异常时,promise对象将变为失败状态,结果为该错误对象(即等同于reject)。
二、Promise的API
2.1 promise的构造函数: new Promise(excutor)
- excutor函数:执行器,(resolve, reject) => {…}。该函数会在创建promise对象时立即
同步调用
,可以把异步操作放入此函数内部执行。 - resolve函数:将promise对象的状态变更为成功,并可以指定成功的结果值
- reject函数:将promise对象的状态变更为失败,并可以指定失败的结果值
2.2 Promise.prototype.then: (onResolved, onRejeced)=>{}
- onResolved:promise对象状态为成功时的回调函数,(value)=>{},value为
调用该then的promise对象的成功结果
- onRejeced:promise对象状态为失败时的回调函数,(reason)=>{},reason为
调用该then的promise对象的失败结果
2.3 Promise.prototype.catch: (onRejeced)=>{}
- onRejeced:promise对象状态为失败时的回调函数,(reason)=>{},reason为
调用该catch的promise对象的失败结果
2.4 Promise的静态方法
- Promise.resolve(value):返回一个成功状态的promise对象;
(1)value不是promise类型数据:状态成功,结果就是value
(2)value是promise类型的数据:直接返回value - Promise.reject(value):返回一个失败状态的promise对象;
(1)value不是promise类型数据:状态失败,结果就是value
(2)value是promise类型的数据:状态失败,返回结果就是value - Promise.all([p1,p2,p3]):返回一个新的promise对象;
(1)p1,p2,p3是promise对象,当它们都成功时,all返回一个成功状态的promise对象,成功结果为p1,p2,p3的结果值组成的数组。
(2)p1,p2,p3中任意一个失败后,all返回一个失败状态的promise对象,失败结果为失败的那个promise对象的失败值。 - Promise.race([p1,p2,p3]):返回一个新的promise对象;
(1)p1,p2,p3中任意一个率先成功后,race返回一个成功状态的promise对象,成功结果为成功的那个promise对象的成功值。
(2)p1,p2,p3中任意一个率先失败后,race返回一个失败状态的promise对象,失败结果为失败的那个promise对象的失败值。
三、Promise的关键问题
3.1 一个promise对象指定多个的回调时,都会调用吗?(不是指链式调用)
答:都会调用
let p =new Promise((resolve,reject)=>{
reject('ok')
})
p.then(res=>{
console.log(res,'111')
},(err)=>{
console.log(err,'333')
})
p.then(res=>{
console.log(res,'222')
},(err)=>{
console.log(err,'444')
})
3.2 改变状态和指定回调函数的先后顺序
注意:then()的作用就是指定回调函数,并等待promise对象改变时,执行指定的回调函数。
- 当在同步代码中执行resolve或reject时,先改变状态,后指定回调函数
let p =new Promise((resolve,reject)=>{
resolve('ok')
})
p.then(res=>{
console.log(res)
},(err)=>{
console.log(err)
})
- 当在异步代码中执行resolve或reject时,先指定回调函数,后改变状态
let p =new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('ok')
}, 3000);
})
p.then(res=>{
console.log(res)
},(err)=>{
console.log(err)
})
3.3 then方法返回的promise对象受什么影响?
前提:成功时调用onResolved, 失败时调用onRejeced
- 函数中没返回值:返回一个成功状态的promise,结果为undefined
- 函数中throw异常:返回一个失败状态的promise,结果为异常值
- 函数中返回了promise类型对象:取决于此对象状态和结果
- 函数中返回了非promise类对象:返回一个成功状态的promise,结果为该对象值
四、Promise自定义封装
五、async与await
为了拿到promise对象的结果,以前需要使用then方法来拿到结果,现在可以使用async await 来直接拿到promise对象的结果。不在需要使用函数调用的方式,直接使用表达式即可。比链式调用更加方便直观
。
then方式:
const fs = require("fs");
function read(url){
return new Promise(function(resolve,reject){
fs.readFile(`./${url}.txt`,function(err,data){
if(err)return;
resolve(data.toString());
});
});
}
read(1).then(function(data){
console.log(data);
read(2).then(function(data){
console.log(data);
read(3).then(function(data){
console.log(data);
read(4).then(function(data){
console.log(data);
read(5).then(function(data){
console.log(data);
read(6).then(function(data){
console.log(data);
});
});
});
});
});
});
// 或者
read(1).then(function(data){
console.log(data);
return read(2)
}).then(function(data){
console.log(data)
return read(3)
}).then(function(data){
console.log(data)
return read(4)
}).then(function(data){
console.log(data)
return read(5)
})
async await方式
async function main(){
var data1 = await read(1);
console.log(data1);
var data2 = await read(2);
console.log(data2);
var data3 = await read(3);
console.log(data3);
var data4 = await read(4);
console.log(data4);
var data5 = await read(5);
console.log(data5);
var data6 = await read(6);
console.log(data6);
}
main();
5.1 async
- 函数的结果是一个promise对象
- 该对象的结果由async函数return的返回值决定
5.2 await
- await 必须写在 async 函数中(es2022中top-level,await可以脱离async使用了), 但 async 函数中可以没有 await。
- 如果 await 的 promise 失败了, 会抛出异常, 需要通过 try…catch 捕获处理