一、Promise
1.定义
Promise对象用于表示一个异步操作的最终完成(或失败)及其结果值
我们用一张图来清晰的看Promise位于哪里
2.好处
1.逻辑更清晰
2.了解axios函数内部运作机制
3.能解决回调函数地狱问题
3.使用语法及步骤
示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>认识Promise</title>
</head>
<body>
<script>
/**
* 目标:使用Promise管理异步任务
*/
//1创建Promise对象
const p = new Promise((resolve, reject) => {
//2执行异步代码
setTimeout(() => {
resolve('模拟AJAX请求-成功结果')
}, 2000);
})
//3 获取结果
p.then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
</script>
</body>
</html>
//1创建Promise对象
const p = new Promise((resolve, reject) => {
//2执行异步代码
setTimeout(() => {
//resolve('模拟AJAX请求-成功结果')
reject(new Error('模拟AJAX请求-失败结果'))
}, 2000);
})
根据例子不难看出当我们成功的时候,会调用resolve函数,失败的时候会调用reject函数
二、Promise的三种状态
1.作用
了解Promise对象如何关联的处理函数,以及代码执行顺序
2.概念
一个Promise对象,必然处于以下几种状态之一
待定(pending):初始状态,既没有被兑现,也没有被拒绝
已兑现(fulfilled):意味着,操作成功完成
已拒绝(rejected):意味着,操作失败
注意:Promise对象一旦被兑现/拒绝就是已敲定了,状态无法再被改变
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>认识Promise状态</title>
</head>
<body>
<script>
/**
* 目标:认识Promise状态
*/
// 1. 创建Promise对象(pending-待定状态)
const p = new Promise((resolve, reject) => {
//Promise对象创建时,这里的代码都会执行了
// 2. 执行异步代码
setTimeout(() => {
//resolve() =>'fulfilled状态-已兑现'=>then()
// resolve('模拟AJAX请求-成功结果')
//reject() =>'rejected状态-已拒绝' =>catch()
reject(new Error('模拟AJAX请求-失败结果'))
}, 2000)
})
console.log(p)
// 3. 获取结果
p.then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
</script>
</body>
</html>
我们打印Promise对象看看
注意:是在异步代码为执行之前查看的,也就是说此时setTimeout函数还并没有执行
我们来看看异步代码执行以后的状态
此时就成了rejected状态,状态改变,就会调回.catch()去执行reject()
反之成功,会触发.then()去执行resolve()
总结来说,当Promise对象状态变为fulfilled,就会去.then()就会触发,反之触发.catch()
三、获取省份列表案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>案例_使用Promise+XHR_获取省份列表</title>
</head>
<body>
<p class="my-p"></p>
<script>
/**
* 目标:使用Promise管理XHR请求省份列表
* 1. 创建Promise对象
* 2. 执行XHR异步代码,获取省份列表
* 3. 关联成功或失败函数,做后续处理
*/
//1. 创建Promise对象
const p = new Promise((resolve, reject) => {
// 2. 执行XHR异步代码,获取省份列表
const xhr = new XMLHttpRequest()
xhr.open('GET', 'https://hmajax.itheima.net/api/province')
//xhr如何判断响应成功还是失败?
//2xx开头的都是成功响应状态码
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
}
else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
//3. 关联成功或失败函数,做后续处理
p.then(result => {
console.log(result)
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
//错误对象要用console.dir详细打印
console.dir(error)
//服务器返回错误提示消息,插入到p标签显示
document.querySelector('.my-p').innerHTML = error.message
})
</script>
</body>
</html>
状态响应码给大家放下面
四、封装_简易axios
1.获取省份列表
因为axios最核心的两个技术已经学过了
所以现在我们可以封装一个简易的axios函数,通过使用我们自己axios函数来实现该功能
话不多说上码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>封装_简易axios函数_获取省份列表</title>
</head>
<body>
<p class="my-p"></p>
<script>
/**
* 目标:封装_简易axios函数_获取省份列表
* 1. 定义myAxios函数,接收配置对象,返回Promise对象
* 2. 发起XHR请求,默认请求方法为GET
* 3. 调用成功/失败的处理程序
* 4. 使用myAxios函数,获取省份列表展示
*/
//1. 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
return new Promise((resolve, reject) => {
//2. 发起XHR请求,默认请求方法为GET
const xhr = new XMLHttpRequest()
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
//3. 调用成功/失败的处理程序
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
// 4. 使用myAxios函数,获取省份列表展示
myAxios({
url: 'https://hmajax.itheima.net/api/province'
}).then(result => {
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
document.querySelector('.my-p').innerHTML = error.message
})
</script>
</body>
</html>
2.获取地区列表
通过上述例子我们发现上述封装的axios函数不支持传递查询参数
所以我们继续改进
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>封装_简易axios函数_获取地区列表</title>
</head>
<body>
<p class="my-p"></p>
<script>
/**
* 目标:封装_简易axios函数_获取地区列表
* 1. 判断有params选项,携带查询参数
* 2. 使用URLSearchParams转换,并携带到url上
* 3. 使用myAxios函数,获取地区列表
*/
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
//1. 判断有无params选项,携带查询参数
if (config.params) {
//2. 使用URLSearchParams转换,并携带到url上
const paramsObj = new URLSearchParams(config.params)
const querString = paramsObj.toString()
config.url += `?${querString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
//3. 使用myAxios函数,获取地区列表
myAxios({
url: 'https://hmajax.itheima.net/api/area',
params: {
pname: '辽宁省',
cname: '大连市'
}
}).then(result => {
console.log(result)
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
})
</script>
</body>
</html>
我们发现相对于第一个例子来说,我们第二个例子里的myAxios函数只多了一段代码
//1. 判断有无params选项,携带查询参数
if (config.params) {
//2. 使用URLSearchParams转换,并携带到url上
const paramsObj = new URLSearchParams(config.params)
const querString = paramsObj.toString()
config.url += `?${querString}`
}
也就是这段用来判断有无params选项的代码
但是我们发现这样还是不够,如果出现下面这种情况阁下又该怎么办呢?
相信根据例2的问题以后大家心里都有思路了
3.注册用户功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>封装_简易axios函数_注册用户</title>
</head>
<body>
<button class="reg-btn">注册用户</button>
<script>
/**
* 目标:封装_简易axios函数_注册用户
* 1. 判断有data选项,携带请求体
* 2. 转换数据类型,在send中发送
* 3. 使用myAxios函数,完成注册用户
*/
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
//1. 判断有data选项,携带请求体
if (config.data) {
// 2. 转换数据类型,在send中发送
const jsonStr = JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
//如果没有请求体数据,正常的发起请求
xhr.send()
}
})
}
const btn = document.querySelector('.reg-btn')
btn.addEventListener('click', () => {
// 3. 使用myAxios函数,完成注册用户
myAxios({
url: 'https://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima999',
password: '666666'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
})
</script>
</body>
</html>
由于我已经注册过该账户了,所以会提示账号被占用了
那么这样我们自己封装的axios函数是不是就完成了呀,我们这样就更能理解axios函数的原理了