什么是回调地狱问题
如果在回调函数中再传入一个函数,就会出现一个嵌套结构如此层层嵌套回调就会形成回调地狱。
业务中有时在请求数据时会出现这种情况,需要发送多个AJAX请求,第1个正常发送,但是第2个需要第1个请求的数据结果中的某一个值作为参数,第3个请求需要第2个请求的数据结果中的某一个值作为参数,如此层层嵌套回调就是回调地狱。这样的代码就很难去维护且业务逻辑复杂代码可读性也不好。
//有多个异步任务,要求需要同时拿到所有异步任务的结果,下边就是用回调地狱
$.get("url", (res1) => {
conosle.log(res1)
$.get("url+res1", (res2) => {
conosle.log(res2)
$.get("url+res2", (res3) => {
conosle.log(res3)
$.get("url+res3", (res4) => {
conosle.log(res4)
})
})
})
})
Promise解决回调函数
Promise是一个对象里面保存着某个未来才会结束的事件就是一个异步操作的结果,从它可以获取异步操作的消息。Promise对象的出现就是进行处理各种异步操作,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数也就可以用于解决回调地狱。
$getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
var sayhello = function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(name);
resolve(); //在异步操作执行完后执行 resolve() 函数
}, 1000);
});
}
sayhello("first").then(function () {
return sayhello("second"); //仍然返回一个 Promise 对象
}).then(function () {
return sayhello("third");
}).then(function () {
console.log('end');
}).catch(function (err) {
console.log(err);
})
//输出:first second third end
采用链式的 then,可以指定一组按照次序调用的回调函数。这时,前一个 then 里的一个回调函数,返回的可能还是一个 Promise
对象(即有异步操作),这时后一个回调函数,就会等待该 Promise
对象的状态发生变化,才会被调用。由此实现异步操作按照次序执行。
第一个then方法传入的回调函数,返回的是另一个Promise对象。这时,第二个then方法传入的回调函数,就会等待这个新的Promise对象状态发生变化,只有新的Promise对象状态改变了才会触发后面代码的执行。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数。这样就将异步操作以同步操作的流程表达出来了.
async/await
async/await其实是Promise的语法糖,它能实现的效果都能用then函数链式调用来实现,它是为优化then函数链式调用而开发出来的。
1.async用于修饰函数,它可以修饰function声明的函数也可以修饰对象的方法以及箭头函数,async表示函数是异步的。async定义的函数内部会默认返回一个Promise对象,如果函数内部抛出异常或者是函数返回reject或者函数内部存在js语法错误,都会使函数的Promise对象状态为失败rejected。async函数接收到返回的值,发现不是异常或者reject,则判定Promise对象状态为成功fulfilled,这里可以return所有js数据类型,字符串,布尔值,数字类型等都是判定Promise对象状态为成功fulfilled。
async function fn(){
return("234");
}
console.log(fn(),111);
async function fm(){
return Promise.reject('执行失败')
}
fm().then((data)=>{
console.log(data,2222);
})
.catch((err)=>{
console.log(err,2222);
});
2.await就表示等待某个操作完成,语法上强制规定await只能出现在asnyc修饰的函数中。await操作符后面可以是任意值。但当await后面是Promise对象的时候,会暂停async修饰的异步函数执行。也就是说,必须得等待await后面的Promise对象处理完成才能继续函数中后面代码的运行,它会阻塞函数中后面的代码但是不会阻碍这个异步函数后面的js代码,等着Promise对象状态改变了,然后得到异步操作返回的结果,就作为await表达式的运算结果。如果它等到的不是一个 Promise对象,那await表达式的运算结果也会将等到的值转换成一个 的Promise对象。则async函数必须等到内部所有的 await 命令的 Promise对象执行完,async函数的返回值Promise对象才会发生状态改变。此时才能执行then方法或者catch方法得到数据。
async function fn(){
let data=await setTimeout(()=>{
return "delay";
},1000);
console .log(123);
return data;
};
console.log(fn(),111);
console.log(456);
3.async修饰的异步函数里如果有多个await的时候,如果其中任意一个抛出异常或者报错了,都会导致函数停止执行,async函数的返回值Promise对象直接rejected,函数中就可以采用try-catch语法。
async function fn(){
let data=await Promise.reject(404);
console.log("not go on")
return 234;
};
函数中就可以采用try-catch语法
async function fn(){
try {
let data=await Promise.reject(404);
console.log("not go on");
} catch (error) {
console.log(error);
} finally{
return "无论怎样都会执行";
}
};
fn().then((data)=>{
console.log(data,111);
})
.catch((err)=>{
console.log(err,222)
});
多次网络请求需要嵌套时避免层层回调使用方法
async function fn(){
let data1=await new Promise((resolve,reject)=>{resolve(2000)})
console.log(data1)
let data2=await axios('/ajax2')
console.log(data2)
let data3=await axios('/ajax3')
console.log(data3)
let data4=await axios('/ajax4')
console.log(data4)
}