1、什么是promise
promise 是一个 ES6 的语法 承诺的意思,是一个专门用来解决异步 回调地狱 的问题
2、什么是回调地狱
- 当一个回调函数嵌套一个回调函数的时候
- 就会出现一个
嵌套结构
- 当嵌套的多了就会出现
回调地狱
的情况 - 比如我们发送三个
ajax
请求
- 第一个正常发送
- 第二个请求需要
第一个请求的结果
中的某一个值作为参数 - 第三个请求需要
第二个请求的结果
中的某一个值作为参数
ajax({
url: "我是第一个请求",
success: function (res) {
console.log(res);//打印第一个请求结果
// 发送第二个请求
ajax({
url: "我是第二个请求",
data: {
name: "张三"
},
success: function (res1) {
console.log(res1);//打印第二个请求结果
//发送第三个请求
ajax({
url: "我是第三个请求",
method: "POST",
data: {
name: "李四",
age: 18
},
success: function (res2) {
console.log(res2);//打印第三个请求结果
}
})
}
})
}
})
- 回调地狱,其实就是回调函数嵌套过多导致的。
- 当代码成为这个结构以后,已经没有维护的可能了
- 所以我们要把代码写的更加的艺术一些。这个时候就需要用到我们的
promise对象
。
3、promise的基本使用
在利用promise对象来解决回调函数之前我们先来了解一下promise的基本使用。
3.1 promise基本语法
- promise 就是一个语法
- 当我们
new
一个promise
,此时我们需要传递一个回调函数
,这个函数为立即执行的,称之为(executor
) - 这个回调函数,我们需要传入两个参数回调函数,
reslove
,reject
(函数可以进行传参)
- 当执行了
reslove
函数,会回调promise对象的.then
函数 - 当执行了
rejec
t函数,会回调promise对象的.catche
函数
举个栗子:
function resultData(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (data === 'list') {
resolve("成功了,数据上传成功")
} else {
reject("失败了,请检查数据是否正确")
}
}, 1000)
})
}
//请求成功
resultData('list').then(resolve => {
console.log(resolve);
})//成功了,数据上传成功
//请求失败
resultData('lit').catch(reject => {
console.log(reject);
})//失败了,请检查数据是否正确
3.2 promise的状态
- 使用promise的时候,给它一个承诺,我们可以将他划分为三个阶段
pending(待定)
,执行了executor,状态还在等待中,没有被兑现,也没有被拒绝fulfilled(已兑现)
,执行了resolve函数则代表了已兑现状态rejected(已拒绝)
,执行了reject函数则代表了已拒绝状态
- 简单理解:
- 你承诺自己明天开始好好学习(还未到明天,此时为
待定状态
) - 时光飞逝,到了第二天你去图书馆好好学习了(
已兑现状态
) - 时光飞逝,到了第二天你因为某些事情而无法去图书馆学习(
已拒绝状态
)
- 状态只要从待定状态,变为其他状态后,则状态不能再改变。
- 比如从
pengding状态
变为fulfilled状态
后,则不能在从fulfilled状态
变为rejected状态
。 - 同样从
pengding状态
变为rejected状态
后,则不能在从rejected状态
变为fulfilled状态
。
举个栗子:
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功了')
reject('失败了')
},1000)
})
p.then(resolve=>console.log(resolve)).catch(reject=>console.log(reject))
//成功了
当我调用
reject
之后,在调用resolve
是无效的,因为状态已经发生改变,并且是不可逆的。
3.3 resolve不同值的区别
resolve() 和reject()只能接受并处理一个参数,多余的参数会被忽略掉。
如果想传递多个数据,需要用数组,或者对象的方式来进行传参。
function resultData(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (data === 'list') {
resolve(["成功了,数据上传成功","你可以进行下一步操作了"])
} else {
reject({one:"失败了,请检查数据是否正确",two:"需要重新上传"})
}
}, 1000)
})
}
//请求成功
resultData('list').then(resolve => {
console.log(resolve[0],resolve[1]);//成功了,数据上传成功 你可以进行下一步操作了
})
//请求失败
resultData('lit').catch(reject => {
console.log(reject.one,reject.two);//失败了,请检查数据是否正确 需要重新上传
})
在正确的时候调用resolve函数,失败的时候调用reject函数,把需要的参数传递出去。
如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态
const result = new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('你好')
}, 1000);
}))
})
result.then(res => console.log(res))
3.4 Promise中的类方法/静态方法
3.4.1 Promise.reslove
Promise.resolve('hello').then(res => console.log(res))//hello
// 等价于
const p = new Promise((resolve, reject) => resolve('hello'))
p.then(res => console.log(res))//hello
有的时候,你预知状态的结果为fulfilled,则可以用这种简写方式
3.4.2 Promise.reject
Promise.reject('hello').catch(err => console.log(err))/hello
// 等价于
const p = new Promise((resolve, reject) => reject('hello'))//hello
p.catch(err => console.log(err))
有的时候,你预知状态的结果为rejected,则可以用这种简写方式
3.4.3 Promise.all
fulfilled
状态
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 张三')
}, 1000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 李四')
}, 2000);
})
const promiseThree = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 王五')
}, 3000);
})
Promise.all([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res))
//['hello 张三', 'hello 李四', 'hello 王五']
- 只有三个都为
resolve
状态的时候才会调用.then方法。 - 只要有一个promise的状态为
rejected
,则会回调.catch方法。
rejected状态
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 张三')
}, 1000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 李四')
}, 2000);
})
const promiseThree = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 王五')
}, 3000);
})
Promise.all([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res)).catch(err=>console.log(err))
- 当遇到rejectd的时候,后续的promise结果我们是获取不到,并且会把
reject的实参
,传递给catch的err形参
中去,所以最后的结果是hello 李四
3.4.4 Promise.allSettled
上面的
Promise.all
有一个缺陷,就是当遇到一个rejected的状态,那么对于后面是resolve或者reject的结果我们都无法拿到。
Promise.allSettled
,无论状态是fulfilled
/rejected
都会把参数返回给我们
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 张三')
}, 1000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 李四')
}, 2000);
})
const promiseThree = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 王五')
}, 3000);
})
Promise.allSettled([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res))
注意:该方法如果其中一个
promise
没有结果
,则会什么都不打印。
简单理解:如果没有调用resolve()
或者reject()
,该promise
就无法得到一个结果
,其中一个没有结果,则什么都不会打印。
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 张三')
}, 1000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 李四')
}, 2000);
})
const promiseThree = new Promise((resolve, reject) => {
setTimeout(() => {
}, 3000);
})
Promise.allSettled([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res))
//其中一个promise没有结果,则什么都不打印
3.4.5 Promise.race
- 优先获取第一个返回的结果,无论结果是
fulfilled
还是rejectd
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 张三')
}, 2000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 李四')
}, 1000);
})
Promise.race([promiseOne, promiseTwo]).then(res => console.log(res)).catch(err=>console.log(err))
结果是hello 李四
3.4.6 Promise.any
- 只获取第一个状态为
fulfilled
,如果全部为rejected
则报错
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 张三')
}, 2000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello 李四')
}, 3000);
})
Promise.any([promiseOne, promiseTwo]).then(res => console.log(res)).catch(err=>console.log(err))
打印的结果是hello 李四
const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 张三')
}, 2000);
})
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hello 李四')
}, 3000);
})
Promise.any([promiseOne, promiseTwo]).then(res => console.log(res)).catch(err=>console.log(err))
直接报错:提醒你AggregateError:所有承诺都被拒绝
4、使用promise解决回调地狱的问题
4.1 promise封装Ajax
//如果data传入的是json格式,而header设置的是form表单编码
//我们就需要将json字符串进行转换。
function queryStringify(obj) {
let str = ''
for (let k in obj) str += `${k}=${obj[k]}&`
//username=kerwin&password=789&
return str.slice(0, -1)
}
// 封装 ajax
function ajax(options) {
//设置一个options默认值,在我们不传递参数的时候默认是get提交
let defaultoptions = {
url: "",
method: "GET",
async: true,
data: {},
headers: {
"content-type": "application/x-www-form-urlencoded"
},
success: function () { },
error: function () { }
}
// 进行解构赋值
let { url, method, async, data, headers, success, error } = {
//展开运算符
...defaultoptions,
...options
}
//判断headers的值是否为json
if (typeof data === 'object' && headers["content-type"]?.indexOf("json") > -1) {
data = JSON.stringify(data)
}
else {
data = queryStringify(data)
}
// // 如果是 get 请求, 并且有参数, 那么直接组装一下 url 信息
if (/^get$/i.test(method) && data) url += '?' + data
// // 4. 发送请求
const xhr = new XMLHttpRequest()
xhr.open(method, url, async)
xhr.onload = function () {
if (!/^2\d{2}$/.test(xhr.status)) {
// console.log(error)
error(`错误状态码:${xhr.status}`) //回调
return
}
// 执行解析
try {
let result = JSON.parse(xhr.responseText)
success(result)
} catch (err) {
error('解析失败 ! 因为后端返回的结果不是 json 格式字符串')
}
}
// // 设置请求头内的信息
for (let k in headers) xhr.setRequestHeader(k, headers[k])
//判断method是不是get
if (/^get$/i.test(method)) {
xhr.send()
} else {
xhr.send(data)
}
}
对此版本封装成promise版本
function pajax(options){
//返回一个promise对象
return new Promise((resolve,reject) => {
ajax({
...options,
success(res){
resolve(res)
},
error(err){
reject(err)
}
})
}
4.2 promise链式调用解决回调地狱
pajax({
url: "我是第一个请求"
}).then(res => {
console.log(res);//打印第一个请求结果
return pajax({
url: "我是第二个请求",
data: {
name: "张三"
}
})
}).then(res => {
console.log(res);//打印第二个请求结果
return pajax({
url: "我是第三个请求",
method: "POST",
data: {
name: "李四",
age: 18
}
})
}).then(res => {
console.log(res);//打印第三个请求结果
}).catch(err=>{
console.log('错误',err);
})
形成一种链式调用,解决了回调地狱的问题。
但是不够优雅,思考一下能不能写成同步的方式呢?
5、async/await
5.1 回调地狱的终极解决方案
异步代码最高的境界就是看起来像同步代码
async function test() {
const res1 = await pajax({
url: "我是第一个请求"
})
const res2 = await pajax({
url: "我是第二个请求",
data: {
name: "张三"
}
})
const res3 = await pajax({
url: "我是第三个请求",
method: "POST",
data: {
name: "李四",
age: 18
}
})
}
5.2 async
语法:
async function test() {
const res = await promise对象
}
- 这个是一个特殊的函数方式
- 可以
await
一个promise
对象 - 可以把
异步代码
写的看起来像同步代码
- 只要是一个
promiser
对象,那么我们就可以使用async/await
来书写
5.2.1 async内部代码同步执行
异步函数的内部代码执行过程
和·普通的函数
是一致的,默认情况下也是会被同步执行
,只在内部产生同步执行,并不会影响函数外面的执行过程。
async function sayHi() {
console.log('我是异步函数1')
console.log('我是异步函数2')
}
console.log('我是外层');
sayHi()
5.2.2 异步函数的返回值
- 普通函数主动返回什么就返回什么,不返回为
undefined
- 异步函数的
返回值特点
明确返回一个
promise
,则由这个promise
决定
5.2.3异步函数的异常处理
- 如果函数内部中途发生错误,可以通过函数的返回值.catch进行捕获
还是拿前面的Ajax案例来举例:
async function test() {
const res1 = await pajax({
url: "我是第一个请求"
})
const res2 = await pajax({
url: "我是第二个请求",
data: {
name: "张三"
}
})
const res3 = await pajax({
url: "我是第三个请求",
method: "POST",
data: {
name: "李四",
age: 18
}
})
}
test().then(res => console.log('成功', res)).catch(err => console.log('失败', err))
- 如果函数内部中途发生错误,也可以通过try catch的方式捕获异常
async function test() {
try {
const res1 = await pajax({
url: "我是第一个请求"
})
const res2 = await pajax({
url: "我是第二个请求",
data: {
name: "张三"
}
})
const res3 = await pajax({
url: "我是第三个请求",
method: "POST",
data: {
name: "李四",
age: 18
}
})
} catch (err) {
console.log('失败', err)
}
}
5.3 await解析
1.await关键字
必须写在async
声明的函数里面
2. 这个promise状态
变为fulfilled
才会执行await
后续的代码,所以await后面的代码,相当于包括在.then方法
的回调中。
- 返回值:
- 如果右侧为promise对象,则返回promise的
fulfilled
结果 - 如果右侧为其他类型的数据则返回
该类型的值
。 - 如果promise是
失败
的状态,则需要在函数内部try catch
,或者进行链式调用进行.catch操作
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/144064.html