Promise深入剖析
文章目录
一、前提须知:
同步异步:
- 现实生活中同步与异步操作
- 同步:烧水10分钟+准备茶叶3分钟 一共花了13分钟
- 异步:在烧水得10分钟里面去准备茶叶 一共花了10分钟
- 软件世界中
- 同步:当一个操作开始执行、主程序需要等它完成、才能继续下一步操作(一般是下一步得程序需要上一步的数据)
- 异步:当一个操作开始执行后、主程序不会等它完成、直接就会执行下一步。(此时该操作可以跟主程序同时并发执行)
回调函数:
-
将函数callback作为参数传入给函数fun、在函数fun中以形参方式进行调用,函数call就称为回调函数。
/* 回调函数 */ function callback(){ console.log('我是一个回调函数'); } /* 普通函数 */ function fun(cb){ cb() //作为形参传进来、然后将形参执行 } // 将回调函数作为参数,传进去 fun(callback) //我是一个回调函数
-
回调地狱()
-
艺术图展示
-
回调地狱、其实就是回调函数嵌套过多导致的
二、Promise的介绍
介绍:
- Promise 是异步编程的一种更优雅的解决方案,比传统的解决方案——回调函数——更合理和更强大。
- Promise就是对异步操作的封装、封装异步文件读写
- 就是一个构造函数、通过new关键字实例化对象、接受函数为参数
特点:
- 对象的状态不受外界影响、Promise代表一个异步操作、有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
- 只有异步操作的结果、可以决定当前是哪一个状态、其他的操作都无法改变这个状态。
- 一旦状态改变、就不会再变、任何时候都可以得到这个结果。Promise对象状态改变只有两种可能
- 从pending变为fulfilled状态
- 从pending变为rejected状态
- 只要发生改变就定型了(ps:一诺千金真男人)
参数梳理:
Promise()
new Promise((resolve,reject)=>{})
- Promise()里面是一个箭头函数、作为参数
- 参数函数里面有两个参数
- resolve:成功函数
- reject:失败函数
Promise实例
const p1 = new Promise((resolve, reject) => {
reject('我是失败结果')// 调用函数, 使当前Promise对象的状态改成rejected
})
-
Promise实例有两个属性
-
PromiseState:状态
-
PromiseResult:结果
-
-
Promise的状态
- pending(等待)
- fulfiled (满足)
- rejected (拒绝)
-
Promise状态改变
- resolve(): 调用函数, 使当前Promise对象的状态改成fulFilled
- reject(): 调用函数,使当前Promise对象状态改成rejected
- 想要输出的结果都在resolve()、reject()里面去写
- 注意事项:
- Promise状态的改变是一次性的,改了就定死了
- 如果不改变Promise状态,那就一直是pending,不会运行其他结果、一直持续那个等待的那个状态。
三、Promise的方法
then方法(搞定它你就懂了)
- 两个参数
- 第一个成功回调函数(fulfiled 状态执行)
- 第二个失败回调函数(rejected 状态执行)
- 注意1:只能通过resolve,reject改变Promise的状态值,从而在then方法里、通过根据状态值来决定执行第一个回调函数、还是执行第二个回调函数(大白话)
- 返回值
- 是一个Promise对象
实例1:不带参数的执行
白话理解:这个promise就是一个标准好男人、只要他结婚了就坚决不会去外面找小三。(先入为主、后面的代码不起作用)
const p = new Promise((resolve,reject)=>{
// 通过调用resolve、传递参数当前Promise对象的结果
reject('这个是失败的结果') //将PromiseState状态改为rejected
resolve('这个是成功的结果') //状态只能先入为主、这个改变不了
})
/* .then方法函数
参数:
1、两个参数都是函数
2、第一个参数是成功回调函数
3、第二个参数是失败回调函数
返回值:
是一个Promise对象
*/
p.then(()=>{
//当Promise的状态使fulfilled时执行
console.log('打印我第一个成功函数');
},()=>{
//当Promise的状态时rejected时, 执行
console.log('打印我第二个失败函数');
})
console.dir(p);
实例2:带参数的执行
- 在then方法的参数函数中、通过形参(value)使用Promsie对象的结果(reslove)
const p = new Promise((resolve,reject)=>{
let obj = {
name:'zjf',
age:19
}
// 通过调用resolve、传递参数当前Promise对象的结果
resolve(obj) //传入一个对象
reject('这个是失败的结果') //将PromiseState状态改为rejected
})
/* .then方法函数
参数:
1、两个参数都是函数
2、第一个参数是成功回调函数
3、第二个参数是失败回调函数
返回值:
是一个Promise对象
*/
p.then((value)=>{
//当Promise的状态使fulfilled时执行
console.log('打印我第一个成功函数');
console.log(value);
},(error)=>{
//当Promise的状态时rejected时, 执行
console.log('打印我第二个失败函数');
})
console.dir(p);
实例3:then方法中的return
- 通过resovle(obj)会传值给,then(成功函数)
- 成功函数中return(obj2),会传值给下一个then(成功函数)
- 成功函数中return(不存在)、会给失败函数的error(result)
- 失败函数中return(obj4)还是会传给成功函数
- 成功函数中不return,只会执行。并没有传值
结论:
- 在then方法中,通过return将返回的Promise实例改为fulfilled状态
- 在then方法中,出现代码错误,将返回的Promise实例改为rejected状态
const p = new Promise((resolve,reject)=>{
let obj = {
name:'obj',
age:19
}
// 通过调用resolve、传递参数当前Promise对象的结果
resolve(obj) //传入一个对象
reject('这个是失败的结果') //将PromiseState状态改为rejected
})
/* .then方法函数
参数:
1、两个参数都是函数
2、第一个参数是成功回调函数
3、第二个参数是失败回调函数
返回值:
是一个Promise对象
*/
p
/* 第一个then方法
使用value接受obj的值
创建一个obj2对象并通过return传值给下一个then
*/
.then((value)=>{
let obj2 = {
name:'obj2',
age:20
}
//当Promise的状态使fulfilled时执行
console.log('打印我第一个then成功函数');
console.log(value); //打印接受到的obj
// 在then方法回返回一个新的Promise实例,状态时pending
return obj2
},(error)=>{
//当Promise的状态时rejected时, 执行
console.log('打印我第一个失败函数');
})
/*
第二个then方法
接受obj2的通过return传来的值
*/
.then((value)=>{
console.log('打印我第二个then成功函数');
console.log(value);
// 返回一个不存在的值
return obj3
},(error)=>{
console.log('打印我第二个then失败函数');
console.log(error);
})
/*
第三个then方法
当接受一个不存在的值(reject)
使用第二个参数函数接受值
*/
.then((value)=>{
console.log(value);
},(error)=>{
let obj4 = {
name:'obj4',
age:22
}
console.log(error);
return obj4
})
/*
第四个then方法
当在第二个函数中、创建一个对象
接受的值还是在第一个函数中接受
*/
.then((value)=>{
let obj5 = {
name:'obj5',
age:33
}
console.log(value); //{name: 'obj4', age: 22}
},(error)=>{
console.log(error);
})
/*
第五个then方法
在第一个函数中、创建一个对象
但是不return这个值
*/
.then((value)=>{
console.log('让我看看第五个then成功');
console.log(value);
},(error)=>{
console.log('让我看看第五个then失败');
})
实例4:状态没有改变、then方法不会执行
// 如果Promise的状态改变,then里的方法不会执行
const p = new Promise((resolve, reject) => {
let a = 1
console.log('我啥也没干啊');
}).then((value) => {
console.log("成功")
},(reason) => {
console.log("失败")
})
//我啥也没干啊
catch方法
- 其实这个方法、就是then方法里面的第二个方法作用一样
- 但是这个方法可以增加代码的可读性,让你一眼就知道这个方法是干嘛的
参数名:根据官方来,最好形参名定为reason
const p = new Promise((resolve, reject) => {
reject()
console.log(a)
})
// 思考: catch中的参数函数在什么时候被执行
// 1. 当Promise的状态改为rejcted.被执行
// 2. 当Promise执行过程出现代码错误时,被执行
p.catch((reason => {
console.log("失败", reason) //失败 undefined
}))
优化代码:此段代码是模仿杰哥课堂中的代码
// 封装ajax请求
function getData(url, data = {}){
return new Promise((resolve, reject) => {
$.ajax({
// 发送请求类型
type: "GET",
url: url,
data: data,
success: function (res) {
// 修改Promise状态为成功, 修改Promise的结果res
resolve(res)
},
error:function (res) {
// 修改Promise的状态为失败,修改Promise的结果res
reject(res)
}
})
}
}
// 调用函数
getData("data1.json")
.then((data) => {
// console.log(data)
const { id } = data
return getData("data2.json", {id})
})
.then((data) => {
// console.log(data)
const { usename } = data
return getData("data3.json", {usename})
})
.then((data) => {
console.log(data)
})
四、Promise.all和Promise.race
Promise.all()
-
将多个实例组装成一个新实例
-
成功的时候返回一个成功的数组
-
失败的时候返回最先被reject失败状态的值
let wake = (time) => {
// 返回一个新的Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time}后返回成功结果`)
}, time);
})
}
// 使用promise去接受
// wake(5).then(
// (res) => {
// console.log(res); //5后返回成功结果
// }
// )
//使用promise.all接受
let p1 = wake(1999);
let p2 = wake(19);
Promise.all([p1, p2]) //返回一个成功的数组
.then(res => {
console.log('返回一个成功数组:',res); //['1999后返回成功结果', '19后返回成功结果']
let [res1, res2] = res; //结构赋值
console.log(res1, res2);
}).catch(err => {
console.log(err);//放回第一个错误
})
Prmise.race
- 将多个实例进行筛选
- 多个实例、那个获取的快、不管是成功、还是失败、先到先得。
let wake = (time) => {
// 返回一个新的Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time}后返回成功结果`)
}, time);
})
}
// 使用promise去接受
// wake(5).then(
// (res) => {
// console.log(res); //5后返回成功结果
// }
// )
//使用promise.all接受
let p1 = wake(1999);
let p2 = wake(19);
Promise.race([p1, p2]) //返回第一个成功的
.then(res => {
console.log('返回第一个成功的:',res); //返回第一个成功的: 19后返回成功结果
}).catch(err => {
console.log(err);
})
五、async、await理解与用法
理解:其实本质就是一个语法糖、ES7提出的基于Promise的解决异步的最终方案。
async:
- 加在函数前的修饰符、将一个普通函数变成返回值变为Promise对象、可以在async函数后面直接.then/.catch方法
- return一个对象data、相当于 return Promise.resolve(data)
- return Promise 得到Promise本身
- 函数内部抛出错误、会被then的第二个函数、catch方法捕获到
// 一个普通的函数加上 async
async function fun(){
let obj = {
name:'zjf',
age:18
}
return obj
}
// 可以将一个普通函数值接写成返回一个promise
fun()
.then((value)=>{
console.log('我是then方法'); //结果我是then方法
console.log(value); //结果{name: 'zjf', age: 18}
})
.catch((reson)=>{
console.log('我是reson方法');
console.log(reson);
})
await:
- await也是一个修饰符、只能放在async定义的函数、理解为等待
- await修饰Promise对象时如等3秒、只能获取到reslove成功函数的值、且等3秒之后才会执行、继续往下执行
- 当出现错误无法捕获、因为await只拿到了resolve成功的、没有reject失败的。
- 如果需要捕获错误、只能通过try、catch方法来捕获错误
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('3000后成功结果')
},3000)
})
// 一个普通的函数加上 async
async function fun(){
let obj = {
name:'zjf',
age:18
}
console.log('我先不等了'); //第一时间就出来了
let reson = await p
console.log(reson); //等到3000后成功结果才出来
}
//执行函数
fun()
在async函数里通过try、catch来获取错误
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('这是失败结果')
},3000)
})
// 一个普通的函数加上 async
async function fun(){
try {
let obj = {
name:'zjf',
age:18
}
console.log('我先不等了'); //第一时间就出来了
let reson = await p
} catch (error) {
console.log(error); //等到3000后成功结果才出来
//这是失败结果
}
}
//执行函数
fun()
在函数外面.catch方法
- 可以接受reject函数里面的值
- 无法接受其他报错、得到的错误
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('这是失败结果')
},3000)
})
// 一个普通的函数加上 async
async function fun(){
let obj = {
name:'zjf',
age:18
}
console.log('我先不等了'); //第一时间就出来了
let reson = await p
}
//执行函数
fun().catch((error)=>{
console.log(error); //这是失败结果
})
在async中是异步执行
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
let obj = {
name:'zjf',
age:18
}
resolve('这是成功结果'+obj)
},3000)
})
// 一个普通的函数加上 async
// 异步任务
async function fun(){
console.log('我先不等了'); //第一时间就出来了
setTimeout(()=>{
console.log('我是5000秒执行'); //最后执行
},5000)
let reson = await p
console.log(reson); //这是成功结果[object Object]
}
//执行函数
fun()
// 运行结果:
// 我先不等了
// 这是成功结果[object Object]
// 我是5000秒执行