回调地狱
前端的ajax和jsonp内部充斥着大量的异步,为了能够拿到异步的数据,使用了大量的回调函数,来获取将来异步执行成功之后的数据。如果请求不多时还好,一旦请求的数量达到一定程度,并且复杂度提升以后,会造成一些问题,这就是回调地狱。
例如:开启三个异步的程序,要求能同时拿到所有异步的结果
//开启三个异步的程序,要求能同时拿到所有异步的结果,下边就是用回调地狱方式解决的例子
ajax({
url:"http://localhost/promise/data/d1.php",
success:function(res1){
console.log(res1);
ajax({
url:"http://localhost/promise/data/d2.php",
success:function(res2){
console.log(res2);
ajax({
url:"http://localhost/promise/data/d3.php",
success:function(res3){
console.log(res3);
console.log(res1, res2, res3);
}
})
}
})
}
})
回调地狱的缺点:
- 上边的例子用回调地狱能解决问题,但是有缺点,或者说不优雅。
- 如果最里面的异步没有执行结束,外面所有的程序是不是都相当于没有结束,有点递归的影子,非常消耗性能。
- 格式非常不优雅,语义化非常差,不方便调错。
回调地狱的解决方法
在js新版本出现之前,回调地狱自身就是解决方式,新版本出现之后,在新的语法中,提供了一些更优雅的处理方案。
1.Promise(ES6)解决
语法:(语言的法律法规,固定!!!!!)
Promise是一个构造函数
new的同时立即传参,参数是回调函数,回调函数身上可以接受两个参数,分别是:resolve(success,成功),reject(error,失败)
var p = new Promise(function(a,b){
// 正在执行....
// 此处放置异步的程序
// a就是在then中的第一个回调函数,表示成功要做的事情
// b就是在catch中的第一个回调函数,表示失败要做的事情
});
p.then(function(){
// 成功的预置函数
});
p.catch(function(){
// 失败的预置函数
});
promis和ajax解决上边的例子
var p1 = new Promise(function(resolve){
ajax({
url:"http://localhost/promise/data/d1.php",
success:function(res1){
resolve(res1);
}
})
})
var p2 = new Promise(function(resolve){
ajax({
url:"http://localhost/promise/data/d2.php",
success:function(res2){
resolve(res2);
}
})
})
var p3 = new Promise(function(resolve){
ajax({
url:"http://localhost/promise/data/d3.php",
success:function(res3){
resolve(res3);
}
})
})
p1.then(function(r1){
console.log(r1);
return p2;
}).then(function(r2){
console.log(r2);
return p3;
}).then(function(r3){
console.log(r3);
}
promise的封装
// ajax函数自身执行的时候不再接收成功和失败的处理函数了
// 交给ajax内部封装的promise处理
function ajax(ops){
ops.type = ops.type || "get";
ops.data = ops.data || {};
var str = "";
for(var key in ops.data){
str += `${key}=${ops.data[key]}&`;
}
if(ops.type=="get"){
let t = new Date().getTime();
ops.url = ops.url + "?" + str + "__qft="+ t;
}
var xhr = new XMLHttpRequest();
xhr.open(ops.type, ops.url);
if(ops.type == "get"){
xhr.send();
}else{
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send(ops.data);
}
return new Promise(function(resolve,reject){
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
resolve(xhr.responseText);
}else if(xhr.readyState === 4 && xhr.status !== 200){
reject("当前请求失败了,失败的原因是:" + xhr.status);
}
}
})
}
当前ajax封装完成之后的调用方式:
// 当前ajax封装完成之后的调用方式:
var p1 = ajax({
type:"get",
url:"",
data:{
user:"admin",
pass:123
}
})
p1.then(function(res){
console.log(res)
})
p1.catch(function(res){
console.log(res)
})
2.async/await(ES7)解决
在Promise的基础上,再使用async/await(ES7)
//语法
async function fn(){
var res1 = await new Promise(....)
var res2 = await new Promise(....)
var res3 = await new Promise(....)
var res4 = await new Promise(....)
console.log(res1,res2,res3,res4);
}
async/await解决上边的例子
async function fn(abc){
var p1 = await ajax({
url:"http://localhost/async-await/data/d1.php"
});
var p2 = await ajax({
url:"http://localhost/async-await/data/d12312.php"
});
var p3 = await ajax({
url:"http://localhost/async-await/data/d3.php"
});
var p3 = await ajax({
url:"http://localhost/async-await/data/d3.php"
});
var p3 = await ajax({
url:"http://localhost/async-await/data/d3.php"
});
console.log(p1, p2, p3);
console.log(abc);
}
document.onclick = function(){
fn("hello world");
}
总结
- promise就是一种固定语法,固定写法,固定传输。
- 所有的原理都在promise的内部被封装。
- promise是用来处理所有的异步的回调地狱,不止是ajax,任何一个异步,只是是回调地狱的调用形式,就可以使用promise改造。