目录
一、回调地狱的定义
所谓的回调地狱 就是在 回调函数中 继续调用执行新的回调函数,本质就是回调函数的嵌套使用。
如:
myAjax( 地址 , 方式 , 参数 , function( response1 ){
根据 第一次的请求结果 再次 发起 第二次请求
myAjax( 地址 , 方式 , 参数 , function( response2 ){
根据 第二次的请求结果 再次 发起 第三次请求
myAjax( 地址 , 方式 , 参数 , function( response3){
根据 第三次的请求结果 执行对应的程序
})
})
})
这就是回调函数的嵌套使用。
二、解决回调地狱的终极目标
将嵌套的语法形式写成并列的语法形式。
myAjax( 地址 , 方式 , 参数 , function( response1 ){})
myAjax( 地址 , 方式 , 参数 , function( response2 ){})
myAjax( 地址 , 方式 , 参数 , function( response3 ){})
但是,Ajax请求是异步程序,如果使用并且的语法会同时开始执行,需要通过JavaScript语法操作让回调函数既写成并列的语法形式又按照顺序一个一个执行。
三、promise语法
3.1 定义
将promise封装成一个函数,函数的return返回值是promise对象,通过 返回值 定义 .then 和 .catch。
3.2 封装promise函数
function 函数( 异步程序需要的参数 ){
const p = new Promise(function(参数1,参数2){
异步程序的执行
执行成功 参数1( 实参 );
执行鼠标 参数2( 实参 );
})
return p ;
}
代码展示:
// 封装一个promise程序执行 ajax请求
// 参数1 请求的url地址
// 参数2 请求的方式
// 参数3 携带的参数怇
function myPromiseAjax( url , type = 'get' , data = '' ){
// 创建一个 promise 对象
const p = new Promise(function( fulfilled , rejected ){
// 执行异步ajax请求
const xhr = new XMLHttpRequest() ;
if( type.toLowerCase() === 'get' ){
// get请求方式
xhr.open( 'get' , `${url}?${data}` );
xhr.send();
}else{
// post请求方式
xhr.open( 'post' , url );
xhr.setRequestHeader('Content-Type' , 'application/x-www-form-urlencoded');
xhr.send(data);
}
// 接收 请求结果
xhr.onreadystatechange = function(){
// 当 ajax状态码是 4 时 判断 http状态码
if( xhr.readyState === 4 ) {
// 如果 http状态码 是 200 - 299
if( /^2\d{2}$/.test( xhr.status ) ){
// 请求成功
fulfilled( xhr.response );
}else if( /^(4|5)\d{2}$/.test( xhr.status ) ){
// 请求失败
rejected( xhr.statusText );
}
}
}
});
// return 返回这个promise对象
return p;
}
3.3 语法形式
第一次
const 变量 = 函数( 异步程序需要的实参 );
变量.then( function( 参数 ){
第一次执行成功对应的函数程序
第二次
return 函数( 异步程序需要的实参 );
})
.then( function( 参数 ){
第二次执行成功对应的函数程序
第三次
return 函数( 异步程序需要的实参 );
})
.then( function(){
第三次执行成功对应的函数程序
})
promise的本质 并不能真的将异步程序写成并列的语法形式 并且按照顺序执行, 本质上 异步程序还是 嵌套的语法形式,只是 通过 .then .catch 将 回调函数 定义在 异步请求之外设定,效果是 看上去 好像是 一个一个执行程序。
代码展示:
<button>请求</button>
<script src="../../../ajax.js"></script>
<script>
// 点击发起请求
const oBtn = document.querySelector('button');
oBtn.addEventListener( 'click' , function(){
// 第一次调用 函数 执行生成 promise对象
// p1 存储的是 第一次请求 生成的 promise实例化对象
const p1 = myPromiseAjax( 'http://localhost:8888/users/register' , 'post' , 'username=123456000&password=123456&rpassword=123456&nickname=123456' );
// 通过 实例化对象.then 定义 请求成功的回调函数
p1.then( function( response1 ){
response1 = JSON.parse( response1 )
console.log( response1 );
// 第二次请求 直接 return 新的请求
return myPromiseAjax( 'http://localhost:8888/users/login' , 'post' , 'username=123456000&password=123456' );
})
// p1.then() 执行结果 就是 一个新的 promise对象也就是 p2
// 可以 写 p1.then().then()
.then(function( response2 ){
response2 = JSON.parse( response2 )
console.log( response2 );
return myPromiseAjax( 'http://localhost:8888/users/logout' , 'get' , `id=${response2.user.id}`);
})
// p1.then().then() 执行结果 又是一个新的promise对象 也就是 p3
// 可以写 p1.then().then().then()
.then( function( response3 ){
response3 = JSON.parse( response3 )
console.log( response3 );
})
// 通过 实例化对象.catch 定义 请求失败的回调函数
p1.catch();
})
</script>
运行结果;
这里用的是老师自己搭建的本地服务器,仅用于教学使用。
四、async 和 await语法
4.1 定义
async 和 await是ES7新增的语法规范,配合promise封装函数执行程序。
4.2 封装async函数
在 async定义的函数中 使用 await 调用 封装的promise函数,执行结果 不再是 return的 promise实例化对象,执行结果 是 异步请求的 响应体数据,以直接定义 下一个 异步请求,await 会 按照顺组一个一个的执行。
代码展示:
// 使用 async 定义封装一个函数
async function getAjax(){
// 使用 await 调用 promise封装的函数
// await 获取的是 异步请求 执行的 响应体结果 不再是 promise对象
// 不再需要 执行 promise对象.then() 执行成功触发的函数程序
// 可以直接写下一次调用
const response1 = JSON.parse( await myPromiseAjax( 'http://localhost:8888/users/register' , 'post' , 'username=123456bbb&password=123456&rpassword=123456&nickname=123456' ) );
console.log( response1 );
// 可以直接定义第二次请求 await 会按照顺序执行程序代码
const response2 = JSON.parse( await myPromiseAjax( 'http://localhost:8888/users/login' , 'post' , 'username=123456bbb&password=123456' ) );
console.log( response2 );
// 可以直接定义第三次请求 await 会按照顺序执行程序代码
const response3 = JSON.parse( await myPromiseAjax( 'http://localhost:8888/users/logout' , 'get' , `id=${response2.user.id}` ) );
console.log( response3 );
}
4.3 语法形式
async function 函数(){
第一次调用
const 变量1 = await promise封装函数() ;
变量1 存储的是 ajax请求的响应体结果
不再是 promise实例化对象
第二次调用
直接调用 不需要在使用 promise对象.then()
await 会按照顺序执行 一个一个的 异步程序
const 变量2 = await promise封装函数() ;
第三次调用
const 变量3 = await promise封装函数() ;
}
代码展示:
<button>请求</button>
<script src="../../../ajax.js"></script>
<script>
const oBtn =document.querySelector('button');
oBtn.addEventListener('click' , function(){
// 调用执行 async 封装的函数
getAjax() ;
})
// 使用 async 定义封装一个函数
async function getAjax(){
// 使用 await 调用 promise封装的函数
// await 获取的是 异步请求 执行的 响应体结果 不再是 promise对象
// 不再需要 执行 promise对象.then() 执行成功触发的函数程序
// 可以直接写下一次调用
const response1 = JSON.parse( await myPromiseAjax( 'http://localhost:8888/users/register' , 'post' , 'username=123456bbb&password=123456&rpassword=123456&nickname=123456' ) );
console.log( response1 );
// 可以直接定义第二次请求 await 会按照顺序执行程序代码
const response2 = JSON.parse( await myPromiseAjax( 'http://localhost:8888/users/login' , 'post' , 'username=123456bbb&password=123456' ) );
console.log( response2 );
// 可以直接定义第三次请求 await 会按照顺序执行程序代码
const response3 = JSON.parse( await myPromiseAjax( 'http://localhost:8888/users/logout' , 'get' , `id=${response2.user.id}` ) );
console.log( response3 );
}
运行结果: