如果没有Promise,我们可能需要这样写:
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then((provinceResult) => {
const pname = provinceResult.data.list[0];
console.log('省份:', pname);
axios({
url: 'http://hmajax.itheima.net/api/city',
params: { pname }
}).then((cityResult) => {
const cname = cityResult.data.list[0];
console.log('城市:', cname);
axios({
url: 'http://hmajax.itheima.net/api/area',
params: { pname, cname }
}).then((areaResult) => {
console.log('地区数据:', areaResult.data);
}).catch((err) => {
console.error('获取地区数据失败:', err);
});
}).catch((err) => {
console.error('获取城市数据失败:', err);
});
}).catch((err) => {
console.error('获取省份数据失败:', err);
});
这种"回调地狱"让代码难以阅读和维护。
而promise给了我们一种拥有较强可读性与灵活性的解决方法:
该示例为axios集成函数,其底层为promise与xhr结合使用,返回值也为promise。
let pname = ''
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then((result) => {
console.log(result);
pname = result.data.list[0]
console.log(result.data.list[0]);
return axios({
url: 'http://hmajax.itheima.net/api/city',
params: {
pname: `${result.data.list[0]}`
}
})
}).then((result => {
console.log(result);
return axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname,
cname: `${result.data.list[0]}`
}
})
})).then(result => {
console.log(result);
})
代码展示了典型的Promise链:
axios.get('/api/province')
.then(result => { // 第一个then
pname = result.data.list[0];
return axios.get('/api/city', {params: {pname}}); // 返回新Promise
})
.then(result => { // 第二个then
return axios.get('/api/area', {params: {pname, cname: result.data.list[0]}});
})
.then(result => { // 第三个then
console.log(result);
});
每个then
接收前一个Promise的解析值,并可以返回新Promise继续传递。
Promise通过链式调用解决了回调嵌套问题,让异步代码拥有了近乎同步代码的可读性。
为了更好理解promise,我们用promise与xhr包装一个简单的axios函数:
同时为了帮助大家加强理解,尽量不使用语义化变量名
function myaxios(lala) {
//promise函数
return new Promise((resolve, reject) => {
//查询模式(有params)
if (lala.params) {
//传递查询参数
const paramsobj = new URLSearchParams(lala.params)
const stree = paramsobj.toString()
lala.url += `?${stree}`
}
//配置xhr
const xhr = new XMLHttpRequest()
xhr.open(lala.method || 'GET', lala.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
console.log(JSON.parse(xhr.response));
resolve(JSON.parse(xhr.response))
}
else {
reject(new Error(xhr.response))
}
})
//请求格式(有data)
if(lala.data){
xhr.setRequestHeader('Contentt-Type','application/json')
const streee=JSON.stringify(lala.data)
xhr.send(streee)
}
else{
xhr.send()
}
})
}
myaxios({
url: 'http://hmajax.itheima.net/api/register',
method:'POST',
data:{
username:'evergreen',
password:'060722'
}
}).then((result) => {
console.log(result);
console.log('注册成功');
}).catch((error) => {
console.log(error);
})
在代码2实现中,myaxios
函数返回一个Promise对象:
function myaxios(lala) {
return new Promise((resolve, reject) => {
// 异步操作
if(成功) resolve(result);
else reject(error);
});
}
关键点:
.then()
都会返回新的Promise,允许继续链式调用在代码1/2中,axios/myaxios返回的Promise解析值均为一个标准响应对象:
{
data: {}, // 服务器返回的数据(自动JSON解析)
status: 200, // HTTP状态码
statusText: 'OK',
headers: {}, // 响应头
config: {}, // 请求配置
request: {} // 底层请求对象
}
axios自动将非2xx状态码视为reject,同时我们也在myaixios中实现:
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
console.log(JSON.parse(xhr.response));
resolve(JSON.parse(xhr.response))
}
else {
reject(new Error(xhr.response))
}
在实现自己的myaxios
时,注意以下几点:
Promise封装错误状态设置:
return new Promise((resolve, reject) => {
// XHR操作
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response));
} else {
reject(new Error(xhr.response));
}
});
参数处理:
// 处理查询参数
if (lala.params) {
//传递查询参数
const paramsobj = new URLSearchParams(lala.params)
const stree = paramsobj.toString()
lala.url += `?${stree}`
}
// 处理请求体
if(lala.data){
xhr.setRequestHeader('Contentt-Type','application/json')
const streee=JSON.stringify(lala.data)
xhr.send(streee)
}
else{
xhr.send()
错误对象:使用new Error()
包装错误信息,在程序而非语义上标记错误信息,便于调试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
let pname = ''
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then((result) => {
console.log(result);
pname = result.data.list[0]
console.log(result.data.list[0]);
return axios({
url: 'http://hmajax.itheima.net/api/city',
params: {
pname: `${result.data.list[0]}`
}
})
}).then((result => {
console.log(result);
return axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname,
cname: `${result.data.list[0]}`
}
})
})).then(result => {
console.log(result);
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/* lala={
url:''
method:''
} */
//要封装的axios
function myaxios(lala) {
//promise函数
return new Promise((resolve, reject) => {
//如果有
if (lala.params) {
//传递查询参数
const paramsobj = new URLSearchParams(lala.params)
const stree = paramsobj.toString()
lala.url += `?${stree}`
}
//配置xhr
const xhr = new XMLHttpRequest()
xhr.open(lala.method || 'GET', lala.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
console.log(JSON.parse(xhr.response));
resolve(JSON.parse(xhr.response))
}
else {
reject(new Error(xhr.response))
}
})
//发送xhr
if(lala.data){
xhr.setRequestHeader('Contentt-Type','application/json')
const streee=JSON.stringify(lala.data)
xhr.send(streee)
}
else{
xhr.send()
}
})
}
myaxios({
url: 'http://hmajax.itheima.net/api/register',
method:'POST',
data:{
username:'evergreen',
password:'060722'
}
}).then((result) => {
console.log(result);
console.log('注册成功');
}).catch((error) => {
console.log(error);
})
</script>
</body>
</html>